Slide 1

Slide 1 text

The evolution of a Cocoa Programmer Chris Eidhof Mobile Central Europe 2014

Slide 2

Slide 2 text

Hashtags #mceconf #mce-evolution

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

commit c416c1d259bddaa74833e6df5acd770f85622293 Author: Chris Eidhof Date: Wed Jan 7 11:54:34 2009 +0100 Empty project

Slide 6

Slide 6 text

I thought I was a pretty good programmer

Slide 7

Slide 7 text

I knew PHP, Ruby and Haskell

Slide 8

Slide 8 text

How hard can it be?

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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;

Slide 11

Slide 11 text

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;

Slide 12

Slide 12 text

There's more

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

How did I get better?

Slide 16

Slide 16 text

If there's only one thing you'll remember Write code for the person after you

Slide 17

Slide 17 text

EDD Embarassment-Driven Development

Slide 18

Slide 18 text

—Write a lot of code —Work with other people —Write articles —Read books and code

Slide 19

Slide 19 text

Write a lot of code —Lots of production code —Lots of throwaway code

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

Collaborate —With people who are better than you —Or who aren't better than you

Slide 22

Slide 22 text

Write Forces you to think.

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Absolute Knowledge

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Perceived Knowledge

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Strive to become better

Slide 29

Slide 29 text

Ship

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Quick Wins

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

I used to do this: if (buttonIndex == 0) instead, now I write if (buttonIndex == StartButton)

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

I used to write this: if ([game state] == running || self.trainingMode) Now I write this: BOOL shouldPauseGame = game.state == running || self.trainingMode; if (shouldPauseGame)

Slide 36

Slide 36 text

Write small files I aim for 100-150 lines

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Write small files find . -name "*.m" -exec wc -l "{}" \; | sort -n

Slide 39

Slide 39 text

Use protocols instead of class inheritance They are much more flexible and make for clearer code.

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Deep code paths Pull out things into separate, well-named methods

Slide 42

Slide 42 text

Don't write fast code Optimize for readability

Slide 43

Slide 43 text

Really learn Interface Builder This makes prototyping much faster

Slide 44

Slide 44 text

Learn your frameworks

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

So last weekend I switched from SQLite/FMDB to Core Data. This may come as a surprise. — Brent Simmons, 05 Oct 2013

Slide 47

Slide 47 text

Core Data —Relationships —Batching —NSFetchedResultsController —Speed of use

Slide 48

Slide 48 text

Value Objects

Slide 49

Slide 49 text

I used NSDictionary for everything NSDate* birthDate = [NSDate dateWithTimeIntervalSince1970:131855*3600]; NSDictionary* person = @{ @"name": @"Chris", @"birthdate": birthDate, @"numberOfKids": @0 };

Slide 50

Slide 50 text

Problems with NSDictionary —It's not typechecked —It might be mutated

Slide 51

Slide 51 text

A first step @interface Person : NSObject @property (nonatomic,copy) NSString* name; @property (nonatomic) NSDate* birthDate; @property (nonatomic) NSUInteger numberOfKids; @end

Slide 52

Slide 52 text

Mutation Sometimes mutation is very handy. Most of the times it will bite you.

Slide 53

Slide 53 text

@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

Slide 54

Slide 54 text

Value Objects —Use lots of them. —Make them immutable.

Slide 55

Slide 55 text

Lighter View Controllers

Slide 56

Slide 56 text

Keep your view controllers light This is the least reusable code you'll write

Slide 57

Slide 57 text

What can you do? —Smarter views —Smarter models —Use view controller transitions —Use categories to populate views —Pull out protocols

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Use categories to populate views @interface MyCell (ConfigureForUser) - (void)configureForUser:(User *)user; @end

Slide 60

Slide 60 text

Use categories to populate views - (void)configureForUser:(User *)user { cell.textLabel.text = user.name; cell.imageView.image = user.avatar; ... }

Slide 61

Slide 61 text

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.

Slide 62

Slide 62 text

Pulling out protocols

Slide 63

Slide 63 text

Pulling out protocols For example: UITableViewDataSource

Slide 64

Slide 64 text

Creating a separate data source object @interface FetchedResultsControllerDataSource : NSObject

Slide 65

Slide 65 text

Creating a separate data source object - (NSInteger)numberOfSectionsInTableView: (UITableView*)t { return self.fetchedResultsController.sections.count; }

Slide 66

Slide 66 text

Creating a separate data source object - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)sectionIndex { id section; section = self.frc.sections[sectionIndex]; return section.numberOfObjects; }

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Advantages of a separate data source —Lighter view controller —Testable —Reusable And you can do this for other protocols, too

Slide 69

Slide 69 text

A separate data source —NSArrayDataSource —NSFRCollectionViewController —...

Slide 70

Slide 70 text

So.... —Write for the person after you —Know your frameworks —Keep your view controllers light

Slide 71

Slide 71 text

Commercial Break

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

Questions? @chriseidhof