Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
はてなにおけるモダンiOSアプリ開発入門
Search
cockscomb
November 27, 2013
Programming
29
19k
はてなにおけるモダンiOSアプリ開発入門
Hatena Engineer Seminar #2 で発表した際のスライドです。
はてなにおけるiOSアプリ開発を説明しました。
cockscomb
November 27, 2013
Tweet
Share
More Decks by cockscomb
See All by cockscomb
jq at the Shortcuts
cockscomb
1
2k
GraphQL放談
cockscomb
4
2.1k
GraphQL Highway
cockscomb
28
8.7k
吉田を支える技術
cockscomb
0
2.4k
コーポレートサイトを静的化してAmplify Consoleにデプロイする
cockscomb
0
3.4k
ユーザインターフェイスと非同期処理
cockscomb
5
2k
GUIアプリケーションの構造と設計
cockscomb
10
10k
イカリング2におけるシングルページアプリケーション
cockscomb
2
7.6k
あなたの知らない UIKit の世界 — UITableView に UITextView を置きたい
cockscomb
1
7.6k
Other Decks in Programming
See All in Programming
Combinatorial Interview Problems with Backtracking Solutions - From Imperative Procedural Programming to Declarative Functional Programming - Part 2
philipschwarz
PRO
0
130
ZJIT: The Ruby 4 JIT Compiler / Ruby Release 30th Anniversary Party
k0kubun
1
310
Developing static sites with Ruby
okuramasafumi
1
340
ゲームの物理 剛体編
fadis
0
390
AI時代を生き抜く 新卒エンジニアの生きる道
coconala_engineer
1
500
Cap'n Webについて
yusukebe
0
160
CSC307 Lecture 02
javiergs
PRO
1
740
20251212 AI 時代的 Legacy Code 營救術 2025 WebConf
mouson
0
240
令和最新版Android Studioで化石デバイス向けアプリを作る
arkw
0
470
[AtCoder Conference 2025] LLMを使った業務AHCの上⼿な解き⽅
terryu16
6
1k
メルカリのリーダビリティチームが取り組む、AI時代のスケーラブルな品質文化
cloverrose
2
440
gunshi
kazupon
1
130
Featured
See All Featured
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
71
Applied NLP in the Age of Generative AI
inesmontani
PRO
3
2k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
1
330
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
39
Designing for humans not robots
tammielis
254
26k
The SEO identity crisis: Don't let AI make you average
varn
0
43
It's Worth the Effort
3n
187
29k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
37
Odyssey Design
rkendrick25
PRO
0
450
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
1
1.4k
Thoughts on Productivity
jonyablonski
73
5k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
61
47k
Transcript
None
ͯͳʹ͓͚Δ ϞμϯiOSΞϓϦ։ൃೖ Hatena Engineer Seminar #2
cockscomb
גࣜձࣾͯͳ ΞϓϦέʔγϣϯΤϯδχΞ ͯͳϒϩάνʔϜ/ͯͳεϖʔενʔϜ ͯͳαϚʔΠϯλʔϯ2013ࢀՃޙ͙͢ʹೖࣾ cockscomb
ͯͳͷΞϓϦ
None
None
None
None
ͯͳϒοΫϚʔΫ iOS SDK
ΞδΣϯμ 1. ͯͳʹ͓͚ΔΞϓϦ։ൃ 2. ͯͳαϚʔΠϯλʔϯ2013 3. ͯͳͷiOS։ൃΛࢧ͑Δٕज़
ͯͳʹ͓͚Δ ΞϓϦ։ൃ
։ൃ HTML5 Titanium RubyMotion ωΠςΟϒ + WebView
ΞϓϦͷํੑΛܾఆ ϖʔύʔϓϩτλΠϐϯά Ϣʔβʔςετ ࣾͷiOSϢʔβʔʹTestFlight ΞϓϦϦϦʔε ϢʔβʔͷಉҙΛಘͯτϥοΩϯά
ͯͳϒϩάΞϓϦ ͔ͬ͠ΓͱϒϩάΛॻ͚Δ͜ͱ ॻ͖͍͢͜ͱ Ͳ͜ʹ͍ͯॻ͚Δ͜ͱ ϑΟʔυόοΫ͕ಘΒΕΔ͜ͱ
None
None
ܭଌ
Web APIΛར༻͢ΔiOSΞϓϦ։ൃ ͯͳαϚʔΠϯλʔϯγοϓ2013
ͯͳΠϯλʔϯ2013 ͯͳαϚʔΠϯλʔϯ ϨϙʔταΠτ ͯͳڭՊॻ J04ߨٛαϯϓϧίʔυ https://github.com/hatena/
–id:murakaming lJ04ଆͬ͟ͱݟ͚ͨͲɺ͜͏͍͏ حྷͳίʔυ͕খ͍͞αϯϓϧͰͳ͘ Ұࣜἧ͍ͬͯΔͷຊʹوॏͩ͠ ༗Γ͗͢Δɻz
–id:griffin-stewie lͦΕͳΓʹ࣮ઓܦݧ͕͋ΔਓͰ ͬͯͳͦ͞͏ͩͬͨΓΒͳͦ͏ͳ͜ͱΛ αϥοͱݟͤͯ͘Ε͍ͯΔz
࣮ફతͳαϯϓϧίʔυ CocoaPods AFNetworking UIStoryboard Auto Layout NSLayoutConstraint Key Value Observation
blocks UITableViewController NSNotification Center isEqual: overriding appledoc …
ͯͳͷiOS։ൃΛ ࢧ͑Δٕज़
CocoaPods platform :ios, '7.0' ! pod 'AFNetworking' pod install
UIStoryboard
- (IBAction)newBookmark:(id)sender { IBKMBookmarkViewController *bookmarkViewController = [[IBKMBookmarkViewController alloc] init]; [self.navigationController
pushViewController:bookmarkViewController animated:YES]; } Before After
Auto Layout
None
@property (weak, nonatomic) IBOutlet UITextView *textView; ! ! ! -
(void)keyboardWillChangeFrame:(NSNotification *)notification { CGRect keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; keyboardRect = [self.view convertRect:keyboardRect fromView:nil]; double animationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; ! CGFloat keyboardHeight = self.view.bounds.size.height - keyboardRect.origin.y; CGRect newRect = self.view.bounds; newRect.size.height -= keyboardHeight; ! [UIView animateWithDuration:animationDuration animations:^{ self.textView.frame = newRect; }]; } Before
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight; ! ! ! -
(void)keyboardWillChangeFrame:(NSNotification *)notification { ... ! CGFloat keyboardHeight = self.view.bounds.size.height - keyboardRect.origin.y; self.keyboardHeight.constant = -keyboardHeight; ! [UIView animateWithDuration:animationDuration animations:^{ [self.view layoutIfNeeded]; }]; } After
Key Value Observation
ϒοΫϚʔΫҰཡ BookmarkManager - (NSArray *)bookmarks TableViewController ࢹ ௨ ߋ৽
TableViewController [[IBKMBookmarkManager sharedManager] reloadBookmarksWithBlock:^(NSError *error) { if (error) { NSLog(@"Error:
%@", error); } [self.tableView reloadData]; }]; खͰஸೡʹUITableViewʹөͤ͞ΔͷΛΊ͍ͨ
TableViewController [[IBKMBookmarkManager sharedManager] reloadBookmarksWithBlock:^(NSError *error) { if (error) { NSLog(@"Error:
%@", error); } }];
Key Value Observation [[IBKMBookmarkManager sharedManager] addObserver:self forKeyPath:@"bookmarks" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if
(object == [IBKMBookmarkManager sharedManager] && [keyPath isEqualToString:@"bookmarks"]) { NSIndexSet *indexSet = change[NSKeyValueChangeIndexesKey]; NSKeyValueChange changeKind = (NSKeyValueChange)[change[NSKeyValueChangeKindKey] integerValue]; ! NSMutableArray *indexPaths = [NSMutableArray array]; [indexSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) { [indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]]; }]; ! [self.tableView beginUpdates]; if (changeKind == NSKeyValueChangeInsertion) { [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; } else if (changeKind == NSKeyValueChangeRemoval) { [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; } else if (changeKind == NSKeyValueChangeReplacement) { [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; } [self.tableView endUpdates]; } }
IBKMBookmarkManager [[self mutableArrayValueForKey:@"bookmarks"] insertObjects:newBookmarks atIndexes:[NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, newBookmarks.count)]]; self.bookmarksΛૢ࡞ͤͣproxyΛܦ༝͢Δ
ϒοΫϚʔΫҰཡ BookmarkManager - (NSArray)bookmarks TableViewController ࢹ ௨ ߋ৽
ςετ
Test Frameworks OCUnit/XCTest Kiwi BDD. ςετΛॻ͖͘͢͢ΔͨΊʹ͍ͬͯΔ Nocilla ωοτϫʔΫStub UIAutomation ౷߹ςετʹΘΕ͍ͯΔ.
KIF͍͖͍ͬͯͨ
context(@"Fetching service data", ^{ it(@"should receive data within one second",
^{ __block NSData *fetchData = nil; NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:@"http://www.hatena.ne.jp/"]]; ! [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { fetchData = data; } ]; [[expectFutureValue(fetchData) shouldEventually] beNonNil]; }); });
stubRequest(@"GET", @"http://www.hatena.ne.jp/"). andReturn(200);
ͯͳ εϚʔτϑΥϯॏࢹ εϚʔτϑΥϯͷτϥϑΟοΫ͕ແࢹͰ͖ͳ͍ େ͖ͳࡋྔ اը͔Βઃܭɺεϐʔυײ͋Δ։ൃ ։ൃख๏ʑมԽ ΤϯδχΞͷࣗओੑͰͲΜͲΜม͍͚͑ͯΔ
ੵۃ࠾༻த http://www.hatena.ne.jp/company/staff
None