Pro Yearly is on sale from $80 to $50! »

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.

68d48587fc806c2b35eb9ff0b7ad8115?s=128

Mike Zornek

April 09, 2015
Tweet

Transcript

  1. SOME CODE PATTERNS

  2. STORYBOARDS

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

  4. // .h extern NSString * const StoryboardNameSettings; // .m NSString

    * const StoryboardNameSettings = @"Settings"; // use new constant UIStoryboard *storyboard = [UIStoryboard storyboardWithName:StoryboardNameSettings bundle:nil];
  5. @implementation UIStoryboard (Additions) + (UIStoryboard *)pch_settingsStoryboard { return [UIStoryboard storyboardWithName:@"Settings"

    bundle:nil]; } @end
  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
  7. CUSTOM ERRORS

  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
  9. BLOCK SAFETY

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

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

    completion(); } }]; }
  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]; }
  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]; }
  14. MAKE A MACRO

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

    })
  16. PROVIDE A DEFAULT BLOCK BEHAVIOR

  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() }
  18. MAKE THE BLOCK REQUIRED

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

  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
  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
  22. TELL, DON'T ASK (DEPENDENCY INJECTION)

  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
  24. None
  25. None
  26. SOME MORE EXAMPLES: ▸ +[NSDate date] ▸ NSNotificationCenter ▸ NSUserDefaults

    ▸ SessionManager ▸ *Store / *Service
  27. But I don't want to manually pass around all these

    state objects! — Someone complained
  28. SOLUTIONS ▸ prepareForSegue:sender: ▸ pushViewController:animated: ▸ ... or a dedicated

    framework
  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/

  30. LAUNCH MENU

  31. None
  32. struct LaunchMenuItem { let title: String let details: String let

    runAction: () -> () } struct LaunchMenuSection { let title: String let items: [LaunchMenuItem] } struct LaunchMenu { let sections: [LaunchMenuSection] }
  33. WEATHERDEMO https://github.com/zorn/WeatherDemo

  34. DATASOURCES FOR *Service OBJECTS

  35. None
  36. None
  37. None
  38. None
  39. None
  40. WEATHERDEMO https://github.com/zorn/WeatherDemo

  41. The End?