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

Some Code Patterns

Some Code Patterns

VIDEO: https://vimeo.com/124773343

This talk covers a handful of code patterns that were successful on my recent projects. Some of these patterns include Block Safety, "Tell, Don't Ask", Using DataSources for your network-based *Service objects.

Mike Zornek

April 09, 2015
Tweet

More Decks by Mike Zornek

Other Decks in Programming

Transcript

  1. SOME CODE
    PATTERNS

    View Slide

  2. STORYBOARDS

    View Slide

  3. UIStoryboard *storyboard =
    [UIStoryboard storyboardWithName:@"Settings" bundle:nil];

    View Slide

  4. // .h
    extern NSString * const StoryboardNameSettings;
    // .m
    NSString * const StoryboardNameSettings = @"Settings";
    // use new constant
    UIStoryboard *storyboard =
    [UIStoryboard storyboardWithName:StoryboardNameSettings bundle:nil];

    View Slide

  5. @implementation UIStoryboard (Additions)
    + (UIStoryboard *)pch_settingsStoryboard
    {
    return [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
    }
    @end

    View Slide

  6. @implementation UIStoryboard (Additions)
    + (UIStoryboard *)pch_settingsStoryboard
    {
    if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
    return [UIStoryboard storyboardWithName:@"Settings~iPad" bundle:nil];
    } else {
    return [UIStoryboard storyboardWithName:@"Settings~iPhone" bundle:nil];
    }
    }
    @end

    View Slide

  7. CUSTOM ERRORS

    View Slide

  8. #define ERROR_DOMAIN @"com.mikezornek.json-parser"
    @implementation NSError (MZJSONParserErrors)
    + (NSError *)mdz_invalidJsonError
    {
    return [NSError errorWithDomain:ERROR_DOMAIN
    code:1001
    userInfo:@{NSLocalizedDescriptionKey :
    @"JSON was malformed and could not be parsed."}];
    }
    @end

    View Slide

  9. BLOCK SAFETY

    View Slide

  10. - (void)tellClientToDoItWithCompletion:(CompletionType)completion
    {
    [self.client doIt:^(NSError *error) {
    completion();
    }];
    }

    View Slide

  11. - (void)tellClientToDoItWithCompletion:(CompletionType)completion
    {
    [self.client doIt:^(NSError *error) {
    if (completion) {
    completion();
    }
    }];
    }

    View Slide

  12. // In even a simple network call this can get out of hand...
    - (void)downloadTracksFromURL:(NSURL *)url completionHandler:(void (^)(NSArray *tracks, NSError *error))completion
    {
    NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url
    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (!error) {
    NSError *trackError;
    NSArray *tracks = [self tracksFromData:data error:&trackError];
    if (tracks) {
    if (completionHandler) {
    completionHandler(tracks, nil);
    }
    } else {
    if (completionHandler) {
    completionHandler(nil, trackError);
    }
    }
    } else {
    if (completionHandler) {
    completionHandler(nil, error);
    }
    }
    }];
    [dataTask resume];
    }

    View Slide

  13. // Look how nicer it gets when you can assume the block is present...
    - (void)downloadTracksFromURL:(NSURL *)url completionHandler:
    (void (^)(NSArray *tracks, NSError *error))completion
    {
    // Some code or method to verify the completion block is not null
    NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url
    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (!error) {
    NSError *trackError;
    NSArray *tracks = [self tracksFromData:data error:&trackError];
    tracks ? completionHandler(tracks, nil) : completionHandler(nil, trackError);
    } else {
    completionHandler(nil, error);
    }
    }];
    [dataTask resume];
    }

    View Slide

  14. MAKE A MACRO

    View Slide

  15. MACROS
    #define BLOCK_CALL(block, ...) ({ if (block) block(__VA_ARGS__); else nil; })

    View Slide

  16. PROVIDE A
    DEFAULT BLOCK
    BEHAVIOR

    View Slide

  17. DEFAULT BLOCK
    - (void)processPhoneList:(NSArray *)list completion:
    (PhoneListCompletionHandler)incomingCompletion
    {
    PhoneListCompletionHandler completion =
    (incomingCompletionHandler != NULL) ? incomingCompletionHandler : ^{
    NSLog(@"processPhoneList: did complete");
    };
    // When the time comes, look ma' no need to check for nil!
    completion()
    }

    View Slide

  18. MAKE THE BLOCK
    REQUIRED

    View Slide

  19. USING ASSERTS
    NSParameterAssert(block != nil);

    View Slide

  20. NEW IN XCODE 6.31
    // as long as the type is a simple object or block pointer.
    - (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
    - (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;
    1 https://developer.apple.com/swift/blog/?id=25

    View Slide

  21. NEW IN XCODE 6.31
    @property (copy, nullable) NSString *name;
    @property (copy, readonly, nonnull) NSArray *allItems;
    1 https://developer.apple.com/swift/blog/?id=25

    View Slide

  22. TELL, DON'T ASK
    (DEPENDENCY
    INJECTION)

    View Slide

  23. The core idea of the Tell, Don't Ask pattern is that any
    *ViewController or *Service or *Store you build should,
    when initialized or otherwise "prepared", have all the information it
    needs to do its job. It should not ask the global state for information.
    -- Mike Zornek, right now

    View Slide

  24. View Slide

  25. View Slide

  26. SOME MORE EXAMPLES:
    ▸ +[NSDate date]
    ▸ NSNotificationCenter
    ▸ NSUserDefaults
    ▸ SessionManager
    ▸ *Store / *Service

    View Slide

  27. But I don't want to manually pass
    around all these state objects!
    — Someone complained

    View Slide

  28. SOLUTIONS
    ▸ prepareForSegue:sender:
    ▸ pushViewController:animated:
    ▸ ... or a dedicated framework

    View Slide

  29. Frameworks
    ▸ http://typhoonframework.org/
    ▸ http://objection-framework.org/
    Articles
    ▸ http://www.objc.io/issue-15/dependency-injection.html
    ▸ https://www.bignerdranch.com/blog/dependency-injection-ios/

    View Slide

  30. LAUNCH MENU

    View Slide

  31. View Slide

  32. struct LaunchMenuItem {
    let title: String
    let details: String
    let runAction: () -> ()
    }
    struct LaunchMenuSection {
    let title: String
    let items: [LaunchMenuItem]
    }
    struct LaunchMenu {
    let sections: [LaunchMenuSection]
    }

    View Slide

  33. WEATHERDEMO
    https://github.com/zorn/WeatherDemo

    View Slide

  34. DATASOURCES
    FOR *Service
    OBJECTS

    View Slide

  35. View Slide

  36. View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. WEATHERDEMO
    https://github.com/zorn/WeatherDemo

    View Slide

  41. The End?

    View Slide