The Evolution of a Cocoa Programmer

The Evolution of a Cocoa Programmer

Ade0c334ecff1448bb96f5f733bf1f83?s=128

Chris Eidhof | @chriseidhof

January 11, 2014
Tweet

Transcript

  1. The evolution of a Cocoa Programmer Chris Eidhof Mobile Central

    Europe 2014
  2. Hashtags #mceconf #mce-evolution

  3. None
  4. None
  5. commit c416c1d259bddaa74833e6df5acd770f85622293 Author: Chris Eidhof <ce@tupil.com> Date: Wed Jan 7

    11:54:34 2009 +0100 Empty project
  6. I thought I was a pretty good programmer

  7. I knew PHP, Ruby and Haskell

  8. How hard can it be?

  9. Example code @interface HiscoreTableViewCell : UITableViewCell { IBOutlet UIImageView *badge;

    IBOutlet UILabel *badgeTitle; IBOutlet UILabel *name1; IBOutlet UILabel *name2; IBOutlet UILabel *name3; IBOutlet UILabel *position1; IBOutlet UILabel *position2; IBOutlet UILabel *position3; IBOutlet UILabel *score1; IBOutlet UILabel *score2; IBOutlet UILabel *score3; }
  10. Example code (continued) @property (nonatomic,readonly) UIImageView* badge; @property (nonatomic,readonly) UILabel

    *badgeTitle; @property (nonatomic,readonly) UILabel *name1; @property (nonatomic,readonly) UILabel *name2; @property (nonatomic,readonly) UILabel *name3; @property (nonatomic,readonly) UILabel *position1; @property (nonatomic,readonly) UILabel *position2; @property (nonatomic,readonly) UILabel *position3; @property (nonatomic,readonly) UILabel *score1;
  11. Example code (continued) @synthesize badge; @synthesize badgeTitle; @synthesize name1; @synthesize

    name2; @synthesize name3; @synthesize position1; @synthesize position2; @synthesize position3; @synthesize score1; @synthesize score2; @synthesize score3;
  12. There's more

  13. //TODO: as of here, it is completely broken if ([gameView

    superview] != nil) { [[SoundBoard sharedSoundBoard] startMenuTune]; [menuViewController viewWillAppear:YES]; [gameViewController viewWillDisappear:YES]; [gameView removeFromSuperview]; [gameViewController viewDidDisappear:YES]; [menuViewController viewDidAppear:YES]; } else {
  14. NSURL *cgiUrl = [NSURL URLWithString:POST_SCORE_URL]; NSMutableURLRequest *p = [NSMutableURLRequest requestWithURL:cgiUrl];

    [p setTimeoutInterval:4]; [p setHTTPMethod:@"POST"]; [p setHTTPBody:requestData]; NSURLResponse *response = nil; NSError *error = nil; NSData *responseData = [NSURLConnection sendSynchronousRequest:p returningResponse:&response error:&error];
  15. How did I get better?

  16. If there's only one thing you'll remember Write code for

    the person after you
  17. EDD Embarassment-Driven Development

  18. —Write a lot of code —Work with other people —Write

    articles —Read books and code
  19. Write a lot of code —Lots of production code —Lots

    of throwaway code
  20. None
  21. Collaborate —With people who are better than you —Or who

    aren't better than you
  22. Write Forces you to think.

  23. Learn your frameworks —Use Core Data —Use UITableViewController —Use NSURLSession

  24. Absolute Knowledge

  25. 2014 1986 1988 1990 1992 1994 1996 1998 2000 2002

    2004 2006 2008 2010 2012 1 2 3 4 5 Time How much I know
  26. Perceived Knowledge

  27. 2014 1986 1988 1990 1992 1994 1996 1998 2000 2002

    2004 2006 2008 2010 2012 1 2 3 4 5 Time How much I know
  28. Strive to become better

  29. Ship

  30. Writing better code —Quick wins —Value Objects —Lighter View Controllers

  31. Quick Wins

  32. I used to do this: if (buttonIndex == 0)

  33. I used to do this: if (buttonIndex == 0) instead,

    now I write if (buttonIndex == StartButton)
  34. I used to write this: if ([game state] == running

    || self.trainingMode)
  35. I used to write this: if ([game state] == running

    || self.trainingMode) Now I write this: BOOL shouldPauseGame = game.state == running || self.trainingMode; if (shouldPauseGame)
  36. Write small files I aim for 100-150 lines

  37. Write small files 325 ./THStyleSheet.m 279 THSkillboxView.m 263 THCardsViewController.m 243

    THTimeLineView.m 221 THScreeningBarViewController.m 192 THEditPriorityViewController.m 185 User+Extensions.m 183 THScreenInstanceViewController.m 183 THRootViewController.m 179 THUserScreenInstancesController.m 168 THPriorityTimelineCollectionController.m 155 UIView+Extensions.m 155 NSArray+Extensions.m 154 THZoomingNavigationController.m
  38. Write small files find . -name "*.m" -exec wc -l

    "{}" \; | sort -n
  39. Use protocols instead of class inheritance They are much more

    flexible and make for clearer code.
  40. Deep code paths - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { if (alertState

    == submitScore && game.score > 0) { // 20 lines if (result != nil && [resultKeys containsObject:@"position"] && [resultKeys containsObject:@"neededPoints"] && [resultKeys containsObject:@"deviceHighscore"] && [resultKeys
  41. Deep code paths Pull out things into separate, well-named methods

  42. Don't write fast code Optimize for readability

  43. Really learn Interface Builder This makes prototyping much faster

  44. Learn your frameworks

  45. What prevents me from using Core Data at this point

    is my concern for scalability and performance. It’s possible I’m just being thick-headed. — Brent Simmons, 27 Sep 2013
  46. So last weekend I switched from SQLite/FMDB to Core Data.

    This may come as a surprise. — Brent Simmons, 05 Oct 2013
  47. Core Data —Relationships —Batching —NSFetchedResultsController —Speed of use

  48. Value Objects

  49. I used NSDictionary for everything NSDate* birthDate = [NSDate dateWithTimeIntervalSince1970:131855*3600];

    NSDictionary* person = @{ @"name": @"Chris", @"birthdate": birthDate, @"numberOfKids": @0 };
  50. Problems with NSDictionary —It's not typechecked —It might be mutated

  51. A first step @interface Person : NSObject @property (nonatomic,copy) NSString*

    name; @property (nonatomic) NSDate* birthDate; @property (nonatomic) NSUInteger numberOfKids; @end
  52. Mutation Sometimes mutation is very handy. Most of the times

    it will bite you.
  53. @interface Person : NSObject @property (nonatomic,readonly) NSString* name; @property (nonatomic,readonly)

    NSDate* birthDate; @property (nonatomic,readonly) NSUInteger numberOfKids; - (instancetype) initWithName:(NSString*)name birthDate:(NSDate*)birthDate numberOfKids:(NSUInteger)numberOfKids; @end
  54. Value Objects —Use lots of them. —Make them immutable.

  55. Lighter View Controllers

  56. Keep your view controllers light This is the least reusable

    code you'll write
  57. What can you do? —Smarter views —Smarter models —Use view

    controller transitions —Use categories to populate views —Pull out protocols
  58. Use categories to populate views - (UITableViewCell*)tableView:(UITableView*)tv cellForRowAtIndexPath:(NSIndexPath*)ip { User

    *user = [self userAtIndexPath:ip]; MyCell *cell = [tv dequeueReusableCellWithIdentifier:ruI forIndexPath:ip]; cell.textLabel.text = user.name; cell.imageView.image = user.avatar; .... }
  59. Use categories to populate views @interface MyCell (ConfigureForUser) - (void)configureForUser:(User

    *)user; @end
  60. Use categories to populate views - (void)configureForUser:(User *)user { cell.textLabel.text

    = user.name; cell.imageView.image = user.avatar; ... }
  61. Use categories to populate views —Your view controller is a

    bit simpler —Your population code can be reused ... —But now your view knows about your model. Sort of.
  62. Pulling out protocols

  63. Pulling out protocols For example: UITableViewDataSource

  64. Creating a separate data source object @interface FetchedResultsControllerDataSource : NSObject

    <UITableViewDataSource, NSFetchedResultsControllerDelegate>
  65. Creating a separate data source object - (NSInteger)numberOfSectionsInTableView: (UITableView*)t {

    return self.fetchedResultsController.sections.count; }
  66. Creating a separate data source object - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)sectionIndex {

    id<NSFetchedResultsSectionInfo> section; section = self.frc.sections[sectionIndex]; return section.numberOfObjects; }
  67. Creating a separate data source object Working with the delegate

    - (UITableViewCell*)tableView:(UITableView*)tv cellForRowAtIndexPath:(NSIndexPath*)ip { id object = [self objectAtIndexPath:indexPath]; id cell = [tv dequeueReusableCellWithIdentifier:ruI forIndexPath:ip]; [self.delegate configureCell:cell withObject:object]; return cell; }
  68. Advantages of a separate data source —Lighter view controller —Testable

    —Reusable And you can do this for other protocols, too
  69. A separate data source —NSArrayDataSource —NSFRCollectionViewController —...

  70. So.... —Write for the person after you —Know your frameworks

    —Keep your view controllers light
  71. Commercial Break

  72. None
  73. Questions? @chriseidhof