Slide 1

Slide 1 text

Lighter View Controllers Chris Eidhof NSSpain 2013 Wednesday, September 18, 13

Slide 2

Slide 2 text

What’s the largest file in your project? Wednesday, September 18, 13

Slide 3

Slide 3 text

What’s the largest file in your project? 0 200 400 600 800 Wednesday, September 18, 13

Slide 4

Slide 4 text

(image by Manu Cornet) Wednesday, September 18, 13

Slide 5

Slide 5 text

(image by Manu Cornet) Wednesday, September 18, 13

Slide 6

Slide 6 text

View controllers are the non-reusable part of your project Wednesday, September 18, 13

Slide 7

Slide 7 text

find . -name "*.m" -exec wc -l "{}" \; | sort -n Wednesday, September 18, 13

Slide 8

Slide 8 text

154 ./ViewControllers/THUserPrioritiesViewController.m 154 ./ViewControllers/THZoomingNavigationController.m 155 ./Extensions/NSArray+Extensions.m 155 ./Extensions/UIView+Extensions.m 168 ./Controllers/THPriorityTimelineCollectionController.m 179 ./Controllers/THUserScreenInstancesController.m 183 ./ViewControllers/THRootViewController.m 183 ./ViewControllers/THScreenInstanceViewController.m 185 ./Model/User+Extensions.m 192 ./ViewControllers/THEditPriorityViewController.m 221 ./ViewControllers/THScreeningBarViewController.m 243 ./Views/THTimeLineView.m 263 ./ViewControllers/THCardsViewController.m 279 ./Views/THSkillboxView.m 325 ./THStyleSheet.m Wednesday, September 18, 13

Slide 9

Slide 9 text

154 ./ViewControllers/THUserPrioritiesViewController.m 154 ./ViewControllers/THZoomingNavigationController.m 155 ./Extensions/NSArray+Extensions.m 155 ./Extensions/UIView+Extensions.m 168 ./Controllers/THPriorityTimelineCollectionController.m 179 ./Controllers/THUserScreenInstancesController.m 183 ./ViewControllers/THRootViewController.m 183 ./ViewControllers/THScreenInstanceViewController.m 185 ./Model/User+Extensions.m 192 ./ViewControllers/THEditPriorityViewController.m 221 ./ViewControllers/THScreeningBarViewController.m 243 ./Views/THTimeLineView.m 263 ./ViewControllers/THCardsViewController.m 279 ./Views/THSkillboxView.m 325 ./THStyleSheet.m Wednesday, September 18, 13

Slide 10

Slide 10 text

... but this view controller is important! Wednesday, September 18, 13

Slide 11

Slide 11 text

Steps Project: A complete OS in <20K lines Wednesday, September 18, 13

Slide 12

Slide 12 text

What can we do? Wednesday, September 18, 13

Slide 13

Slide 13 text

Wednesday, September 18, 13

Slide 14

Slide 14 text

A view controller intermediates between model and view Wednesday, September 18, 13

Slide 15

Slide 15 text

Model logic Wednesday, September 18, 13

Slide 16

Slide 16 text

View Logic Wednesday, September 18, 13

Slide 17

Slide 17 text

View Layout Wednesday, September 18, 13

Slide 18

Slide 18 text

Webservice Wednesday, September 18, 13

Slide 19

Slide 19 text

View Controller View Child View Controller Model Webservice Wednesday, September 18, 13

Slide 20

Slide 20 text

Demo Wednesday, September 18, 13

Slide 21

Slide 21 text

View Controller View Child View Controller Model Webservice Wednesday, September 18, 13

Slide 22

Slide 22 text

View Controller View Child View Controller Model Webservice Wednesday, September 18, 13

Slide 23

Slide 23 text

Model Logic Move into model subclass or into a separate class Wednesday, September 18, 13

Slide 24

Slide 24 text

Some Stats 18 ./NestedTodoList/main.m 30 ./Item.m 31 ./NestedTodoList/Store.m 49 ./NestedTodoList/PersistentStack.m 52 ./NestedTodoList/AppDelegate.m 255 ./NestedTodoList/ItemViewController.m Wednesday, September 18, 13

Slide 25

Slide 25 text

What does it do? ‣ View Setup ‣ Table View Delegate ‣ Table View Datasource ‣ Fetched Results Controller Delegate ‣ Adding items ‣ Deleting Items ‣ ... Wednesday, September 18, 13

Slide 26

Slide 26 text

Custom Datasource What does it do? ‣ View Setup ‣ Table View Delegate ‣ Table View Datasource ‣ Fetched Results Controller Delegate ‣ Adding items ‣ Deleting Items ‣ ... Wednesday, September 18, 13

Slide 27

Slide 27 text

Custom Datasource Model What does it do? ‣ View Setup ‣ Table View Delegate ‣ Table View Datasource ‣ Fetched Results Controller Delegate ‣ Adding items ‣ Deleting Items ‣ ... Wednesday, September 18, 13

Slide 28

Slide 28 text

Model - (BOOL)textFieldShouldReturn:(UITextField*)t { NSString* title = t.text; NSUInteger order = self.parent.children.count; Item* item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.moc]; item.title = title; item.parent = self.parent; item.order = @(order); ... } Wednesday, September 18, 13

Slide 29

Slide 29 text

Model - (BOOL)textFieldShouldReturn:(UITextField*)t { NSString* title = t.text; [Item insertItemWithTitle:title parent:self.parent inManagedObjectContext:self.moc]; ... } Wednesday, September 18, 13

Slide 30

Slide 30 text

- (void)deleteItem:(id)object { Item* item = object; NSSet* siblings = item.parent.children; NSPredicate* predicate = [NSPredicate pre NSSet* itemsAfterSelf = [siblings filtere [itemsAfterSelf enumerateObjectsUsingBloc { sibling.order = @(sibling.order.integ }]; [item.moc deleteObject:item]; } Wednesday, September 18, 13

Slide 31

Slide 31 text

- (void)deleteItem:(id)object { Item* item = object; NSSet* siblings = item.parent.children; NSPredicate* predicate = [NSPredicate pre NSSet* itemsAfterSelf = [siblings filtere [itemsAfterSelf enumerateObjectsUsingBloc { sibling.order = @(sibling.order.integ }]; [item.moc deleteObject:item]; } Wednesday, September 18, 13

Slide 32

Slide 32 text

- (void)prepareForDeletion { NSSet* siblings = self.parent.children; NSPredicate* p = [NSPredicate predicateWithFormat:@"order > %@", self.order]; NSSet* itemsAfterSelf = [siblings filteredSetUsingPredicate:p]; [itemsAfterSelf enumerateObjectsUsingBlock: ^(Item* sibling, BOOL* stop) { sibling.order = @(sibling.order.integerValue - 1); }]; } Wednesday, September 18, 13

Slide 33

Slide 33 text

Some Stats 18 ./NestedTodoList/main.m 31 ./NestedTodoList/Store.m 49 ./NestedTodoList/PersistentStack.m 52 ./NestedTodoList/AppDelegate.m 54 ./Item.m 241 ./NestedTodoList/ItemViewController.m Wednesday, September 18, 13

Slide 34

Slide 34 text

Wednesday, September 18, 13

Slide 35

Slide 35 text

View Controller View Child View Controller Model Webservice UITableViewDS Wednesday, September 18, 13

Slide 36

Slide 36 text

View Controller View Child View Controller Model Webservice UITableViewDS Wednesday, September 18, 13

Slide 37

Slide 37 text

@interface FetchedResultsControllerDataSource : NSObject Wednesday, September 18, 13

Slide 38

Slide 38 text

#pragma mark UITableViewDatasource Wednesday, September 18, 13

Slide 39

Slide 39 text

- (NSInteger)numberOfSectionsInTableView:(UITableView*)t { return self.fetchedResultsController.sections.count; } Wednesday, September 18, 13

Slide 40

Slide 40 text

- (NSInteger)tableView:(UITableView*)t numberOfRowsInSection:(NSInteger)s { id section = self.fetchedResultsController.sections[s]; return section.numberOfObjects; } Wednesday, September 18, 13

Slide 41

Slide 41 text

- (UITableViewCell*)tableView:(UITableView*)t cellForRowAtIndexPath:(NSIndexPath*)ip Wednesday, September 18, 13

Slide 42

Slide 42 text

#pragma mark NSFetchedResultsControllerDelegate Wednesday, September 18, 13

Slide 43

Slide 43 text

- (void)controller:(NSFetchedResultsController*)c didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)ct newIndexPath:(NSIndexPath*)newIndexPath Wednesday, September 18, 13

Slide 44

Slide 44 text

@property (nonatomic, weak) id delegate; Wednesday, September 18, 13

Slide 45

Slide 45 text

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { id object = [self.frc objectAtIndexPath:indexPath]; id cell = [tableView dequeueReusableCellWithIdentifier:self.reuseIdentifier forIndexPath:indexPath]; [self.delegate configureCell:cell withObject:object]; return cell; } Wednesday, September 18, 13

Slide 46

Slide 46 text

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { id object = [self.frc objectAtIndexPath:indexPath]; id cell = [tableView dequeueReusableCellWithIdentifier:self.reuseIdentifier forIndexPath:indexPath]; [self.delegate configureCell:cell withObject:object]; return cell; } Wednesday, September 18, 13

Slide 47

Slide 47 text

Some Stats (3) 25 ./NestedTodoList/Store.m 49 ./NestedTodoList/PersistentStack.m 52 ./NestedTodoList/AppDelegate.m 64 ./Item.m 111 ./NestedTodoList/FRCDataSource.m 162 ./NestedTodoList/ItemViewController.m Wednesday, September 18, 13

Slide 48

Slide 48 text

Code re-use ☺ Wednesday, September 18, 13

Slide 49

Slide 49 text

Other Candidates UICollectionViewDataSource UIPageViewControllerDataSource UIPickerViewDataSource Wednesday, September 18, 13

Slide 50

Slide 50 text

More candidates UICollectionViewDelegate UIScrollViewDelegate UITableViewDelegate UITableViewDelegate UITextFieldDelegate Wednesday, September 18, 13

Slide 51

Slide 51 text

... or use a super class? Wednesday, September 18, 13

Slide 52

Slide 52 text

Superclass disadvantages You only have 1 superclass Harder to switch from table view to collection view Easier to make mistakes: e.g. forget to call super methods. Wednesday, September 18, 13

Slide 53

Slide 53 text

Superclass disadvantages You only have 1 superclass Harder to switch from table view to collection view Easier to make mistakes: e.g. forget to call super methods. Wednesday, September 18, 13

Slide 54

Slide 54 text

Superclass disadvantages You only have 1 superclass Harder to switch from table view to collection view Easier to make mistakes: e.g. forget to call super methods. objc_requires_super Wednesday, September 18, 13

Slide 55

Slide 55 text

Superclass disadvantages You only have 1 superclass Harder to switch from table view to collection view Easier to make mistakes: e.g. forget to call super methods. objc_requires_super Wednesday, September 18, 13

Slide 56

Slide 56 text

View Controller View Child View Controller Model Webservice Wednesday, September 18, 13

Slide 57

Slide 57 text

View Controller View Child View Controller Model Webservice Wednesday, September 18, 13

Slide 58

Slide 58 text

- (UITableViewCell*)tableView:(UITableView*)t cellForRowAtIndexPath:(NSIndexPath*)ip Wednesday, September 18, 13

Slide 59

Slide 59 text

// PhotoCell+ConfigureForPhoto.h @interface PhotoCell (ConfigureForPhoto) - (void)configureForPhoto:(Photo *)photo; @end Wednesday, September 18, 13

Slide 60

Slide 60 text

UITableViewController ‣ Clears selection ‣ Reloads data ‣ Flashes scroll indicators ‣ Editing ‣ Auto-scrolls to first responder ‣ Refresh control ‣ .... Wednesday, September 18, 13

Slide 61

Slide 61 text

Wednesday, September 18, 13

Slide 62

Slide 62 text

Wednesday, September 18, 13

Slide 63

Slide 63 text

View Controller Containment Wednesday, September 18, 13

Slide 64

Slide 64 text

Wednesday, September 18, 13

Slide 65

Slide 65 text

... or in code [self addChildViewController:sliderController]; [self.view addSubview:sliderController]; [UIView animateWithDuration:0.25 animations:^{ sliderController.view.alpha = 1; [sliderController didMoveToParentViewController:self]; }]; Wednesday, September 18, 13

Slide 66

Slide 66 text

Communication Wednesday, September 18, 13

Slide 67

Slide 67 text

Delegate Blocks KVO Wednesday, September 18, 13

Slide 68

Slide 68 text

Too much overhead? Wednesday, September 18, 13

Slide 69

Slide 69 text

Interface Builder Wednesday, September 18, 13

Slide 70

Slide 70 text

Wednesday, September 18, 13

Slide 71

Slide 71 text

Wednesday, September 18, 13

Slide 72

Slide 72 text

Wednesday, September 18, 13

Slide 73

Slide 73 text

Wednesday, September 18, 13

Slide 74

Slide 74 text

@interface MyCell : UITableViewCell @property (weak) IBOutlet UISlider *slider; @property (weak) IBOutlet UISwitch *switch; @end Wednesday, September 18, 13

Slide 75

Slide 75 text

Custom objects in your nib Wednesday, September 18, 13

Slide 76

Slide 76 text

Wednesday, September 18, 13

Slide 77

Slide 77 text

Wednesday, September 18, 13

Slide 78

Slide 78 text

Wednesday, September 18, 13

Slide 79

Slide 79 text

Wednesday, September 18, 13

Slide 80

Slide 80 text

Wednesday, September 18, 13

Slide 81

Slide 81 text

Outlets also work Wednesday, September 18, 13

Slide 82

Slide 82 text

@interface NSObject(UINibLoadingAdditions) - (void)awakeFromNib; @end Wednesday, September 18, 13

Slide 83

Slide 83 text

Testing Wednesday, September 18, 13

Slide 84

Slide 84 text

Testing Wednesday, September 18, 13

Slide 85

Slide 85 text

Testing Wednesday, September 18, 13

Slide 86

Slide 86 text

Separate data source is easy to test Wednesday, September 18, 13

Slide 87

Slide 87 text

Category on cell is easy to test Wednesday, September 18, 13

Slide 88

Slide 88 text

View Controllers are hard to test Wednesday, September 18, 13

Slide 89

Slide 89 text

- (void)testNibLoading; { id mockNavController = [self autoVerifiedMockForClass: [UINavigationController class]]; PhotosViewController *photosViewController = [[PhotosViewController alloc] init]; id photosViewControllerMock = [self autoVerifiedPartialMockForObject: photosViewController]; [[[photosViewControllerMock stub] andReturn:mockNavController] navigationController]; Wednesday, September 18, 13

Slide 90

Slide 90 text

Tools Sort files by length Know your libraries Refactor: Use XCode 5 AppCode Wednesday, September 18, 13

Slide 91

Slide 91 text

Tools Pull out data sources Use View Controller Containment Move code to models and views Wednesday, September 18, 13

Slide 92

Slide 92 text

About Me @chriseidhof http://www.objc.io http://www.uikonf.com http://chris.eidhof.nl Wednesday, September 18, 13