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
1.9k
GraphQL放談
cockscomb
4
2k
GraphQL Highway
cockscomb
28
8.5k
吉田を支える技術
cockscomb
0
2.4k
コーポレートサイトを静的化してAmplify Consoleにデプロイする
cockscomb
0
3.4k
ユーザインターフェイスと非同期処理
cockscomb
5
1.9k
GUIアプリケーションの構造と設計
cockscomb
10
10k
イカリング2におけるシングルページアプリケーション
cockscomb
2
7.6k
あなたの知らない UIKit の世界 — UITableView に UITextView を置きたい
cockscomb
1
7.5k
Other Decks in Programming
See All in Programming
Navigating Dependency Injection with Metro
zacsweers
3
2.5k
複雑なドメインに挑む.pdf
yukisakai1225
5
1.2k
プロパティベーステストによるUIテスト: LLMによるプロパティ定義生成でエッジケースを捉える
tetta_pdnt
0
3.3k
Compose Multiplatform × AI で作る、次世代アプリ開発支援ツールの設計と実装
thagikura
0
170
Android 16 × Jetpack Composeで縦書きテキストエディタを作ろう / Vertical Text Editor with Compose on Android 16
cc4966
2
260
「待たせ上手」なスケルトンスクリーン、 そのUXの裏側
teamlab
PRO
0
560
Navigation 2 を 3 に移行する(予定)ためにやったこと
yokomii
0
330
250830 IaCの選定~AWS SAMのLambdaをECSに乗り換えたときの備忘録~
east_takumi
0
400
楽して成果を出すためのセルフリソース管理
clipnote
0
180
MCPでVibe Working。そして、結局はContext Eng(略)/ Working with Vibe on MCP And Context Eng
rkaga
5
2.3k
How Android Uses Data Structures Behind The Scenes
l2hyunwoo
0
480
複雑なフォームに立ち向かう Next.js の技術選定
macchiitaka
2
180
Featured
See All Featured
Building Applications with DynamoDB
mza
96
6.6k
Speed Design
sergeychernyshev
32
1.1k
Writing Fast Ruby
sferik
628
62k
Making Projects Easy
brettharned
117
6.4k
[RailsConf 2023] Rails as a piece of cake
palkan
57
5.8k
Scaling GitHub
holman
463
140k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
358
30k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Docker and Python
trallard
46
3.6k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Build your cross-platform service in a week with App Engine
jlugia
231
18k
KATA
mclloyd
32
14k
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