$30 off During Our Annual Pro Sale. View Details »

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

cockscomb
November 27, 2013

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

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

cockscomb

November 27, 2013
Tweet

More Decks by cockscomb

Other Decks in Programming

Transcript

  1. View Slide

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

    View Slide

  3. cockscomb

    View Slide

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

    View Slide

  5. ͸ͯͳͷΞϓϦ

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. ͸ͯͳϒοΫϚʔΫ
    iOS SDK

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. View Slide

  17. View Slide

  18. ܭଌ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. ࣮ફతͳαϯϓϧίʔυ
    CocoaPods
    AFNetworking
    UIStoryboard
    Auto Layout
    NSLayoutConstraint
    Key Value Observation
    blocks
    UITableViewController
    NSNotification Center
    isEqual: overriding
    appledoc

    View Slide

  24. ͸ͯͳͷiOS։ൃΛ
    ࢧ͑Δٕज़

    View Slide

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

    View Slide

  26. UIStoryboard

    View Slide

  27. - (IBAction)newBookmark:(id)sender
    {
    IBKMBookmarkViewController *bookmarkViewController
    = [[IBKMBookmarkViewController alloc] init];
    [self.navigationController
    pushViewController:bookmarkViewController
    animated:YES];
    }
    Before
    After

    View Slide

  28. Auto Layout

    View Slide

  29. View Slide

  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

    View Slide

  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

    View Slide

  32. Key Value Observation

    View Slide

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

    View Slide

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

    View Slide

  35. TableViewController
    [[IBKMBookmarkManager sharedManager]
    reloadBookmarksWithBlock:^(NSError *error) {
    if (error) {
    NSLog(@"Error: %@", error);
    }
    }];

    View Slide

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

    View Slide

  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];
    }
    }

    View Slide

  38. IBKMBookmarkManager
    [[self mutableArrayValueForKey:@"bookmarks"]
    insertObjects:newBookmarks
    atIndexes:[NSIndexSet
    indexSetWithIndexesInRange:
    NSMakeRange(0, newBookmarks.count)]];
    self.bookmarksΛ௚઀ૢ࡞ͤͣproxyΛܦ༝͢Δ

    View Slide

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

    View Slide

  40. ςετ

    View Slide

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

    View Slide

  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];
    });
    });

    View Slide

  43. stubRequest(@"GET", @"http://www.hatena.ne.jp/").
    andReturn(200);

    View Slide

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

    View Slide

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

    View Slide

  46. View Slide