はてなにおけるモダンiOSアプリ開発入門

58b40ae3b0db6cf0202a3802e9dd70fe?s=47 cockscomb
November 27, 2013

 はてなにおけるモダンiOSアプリ開発入門

Hatena Engineer Seminar #2 で発表した際のスライドです。
はてなにおけるiOSアプリ開発を説明しました。

58b40ae3b0db6cf0202a3802e9dd70fe?s=128

cockscomb

November 27, 2013
Tweet

Transcript

  1. None
  2. ͸ͯͳʹ͓͚Δ ϞμϯiOSΞϓϦ։ൃೖ໳ Hatena Engineer Seminar #2

  3. cockscomb

  4. גࣜձࣾ͸ͯͳ ΞϓϦέʔγϣϯΤϯδχΞ ͸ͯͳϒϩάνʔϜ/͸ͯͳεϖʔενʔϜ ͸ͯͳαϚʔΠϯλʔϯ2013ࢀՃޙ͙͢ʹೖࣾ cockscomb

  5. ͸ͯͳͷΞϓϦ

  6. None
  7. None
  8. None
  9. None
  10. ͸ͯͳϒοΫϚʔΫ iOS SDK

  11. ΞδΣϯμ 1. ͸ͯͳʹ͓͚ΔΞϓϦ։ൃ 2. ͸ͯͳαϚʔΠϯλʔϯ2013 3. ͸ͯͳͷiOS։ൃΛࢧ͑Δٕज़

  12. ͸ͯͳʹ͓͚Δ ΞϓϦ։ൃ

  13. ։ൃ HTML5 Titanium RubyMotion ωΠςΟϒ + WebView

  14. ΞϓϦͷํ޲ੑΛܾఆ ϖʔύʔϓϩτλΠϐϯά Ϣʔβʔςετ ࣾ಺ͷiOS୺຤ϢʔβʔʹTestFlight ΞϓϦϦϦʔε ϢʔβʔͷಉҙΛಘͯτϥοΩϯά

  15. ͸ͯͳϒϩάΞϓϦ ͔ͬ͠ΓͱϒϩάΛॻ͚Δ͜ͱ  ॻ͖΍͍͢͜ͱ  Ͳ͜ʹ͍ͯ΋ॻ͚Δ͜ͱ ϑΟʔυόοΫ͕ಘΒΕΔ͜ͱ

  16. None
  17. None
  18. ܭଌ

  19. Web APIΛར༻͢ΔiOSΞϓϦ։ൃ ͸ͯͳαϚʔΠϯλʔϯγοϓ2013

  20. ͸ͯͳΠϯλʔϯ2013 ͸ͯͳαϚʔΠϯλʔϯ ϨϙʔταΠτ ͸ͯͳڭՊॻ J04ߨٛαϯϓϧίʔυ https://github.com/hatena/

  21. –id:murakaming lJ04ଆͬ͟ͱݟ͚ͨͲɺ͜͏͍͏ حྷͳίʔυ͕খ͍͞αϯϓϧͰͳ͘ Ұࣜἧ͍ͬͯΔͷ͸ຊ౰ʹوॏͩ͠ ༗Γ೉͗͢Δɻz

  22. –id:griffin-stewie lͦΕͳΓʹ࣮ઓܦݧ͕͋ΔਓͰ΋ ΍ͬͯͳͦ͞͏ͩͬͨΓ஌Βͳͦ͏ͳ͜ͱΛ αϥοͱݟͤͯ͘Ε͍ͯΔz

  23. ࣮ફతͳαϯϓϧίʔυ CocoaPods AFNetworking UIStoryboard Auto Layout NSLayoutConstraint Key Value Observation

    blocks UITableViewController NSNotification Center isEqual: overriding appledoc …
  24. ͸ͯͳͷiOS։ൃΛ ࢧ͑Δٕज़

  25. CocoaPods platform :ios, '7.0' ! pod 'AFNetworking' pod install

  26. UIStoryboard

  27. - (IBAction)newBookmark:(id)sender { IBKMBookmarkViewController *bookmarkViewController = [[IBKMBookmarkViewController alloc] init]; [self.navigationController

    pushViewController:bookmarkViewController animated:YES]; } Before After
  28. Auto Layout

  29. None
  30. @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
  31. @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
  32. Key Value Observation

  33. ϒοΫϚʔΫҰཡ BookmarkManager - (NSArray *)bookmarks TableViewController ؂ࢹ ௨஌ ߋ৽

  34. TableViewController [[IBKMBookmarkManager sharedManager] reloadBookmarksWithBlock:^(NSError *error) { if (error) { NSLog(@"Error:

    %@", error); } [self.tableView reloadData]; }]; खͰஸೡʹUITableViewʹ൓өͤ͞ΔͷΛ΍Ί͍ͨ
  35. TableViewController [[IBKMBookmarkManager sharedManager] reloadBookmarksWithBlock:^(NSError *error) { if (error) { NSLog(@"Error:

    %@", error); } }];
  36. Key Value Observation [[IBKMBookmarkManager sharedManager] addObserver:self forKeyPath:@"bookmarks" options:NSKeyValueObservingOptionNew context:nil];

  37. - (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]; } }
  38. IBKMBookmarkManager [[self mutableArrayValueForKey:@"bookmarks"] insertObjects:newBookmarks atIndexes:[NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, newBookmarks.count)]]; self.bookmarksΛ௚઀ૢ࡞ͤͣproxyΛܦ༝͢Δ

  39. ϒοΫϚʔΫҰཡ BookmarkManager - (NSArray)bookmarks TableViewController ؂ࢹ ௨஌ ߋ৽

  40. ςετ

  41. Test Frameworks OCUnit/XCTest Kiwi BDD. ςετΛॻ͖΍͘͢͢ΔͨΊʹ࢖͍ͬͯΔ Nocilla ωοτϫʔΫStub UIAutomation ౷߹ςετʹ࢖ΘΕ͍ͯΔ.

    KIF΋࢖͍͖͍ͬͯͨ
  42. 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]; }); });
  43. stubRequest(@"GET", @"http://www.hatena.ne.jp/"). andReturn(200);

  44. ͸ͯͳ εϚʔτϑΥϯॏࢹ εϚʔτϑΥϯͷτϥϑΟοΫ͕ແࢹͰ͖ͳ͍ େ͖ͳࡋྔ اը͔Βઃܭɺεϐʔυײ͋Δ։ൃ ։ൃख๏΋೔ʑมԽ ΤϯδχΞͷࣗओੑͰͲΜͲΜม͍͚͑ͯΔ

  45. ੵۃ࠾༻த http://www.hatena.ne.jp/company/staff

  46. None