Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

10 tips for a reusable architecture

5,505 views

Published on

Get some hints on how to write the code of your iOS apps in a more reusable way. This will make you more efficient and happier.

Published in: Mobile

10 tips for a reusable architecture

  1. 1. 10 tips for a reusable architecture Jorge D. Ortiz Fuentes @jdortiz
  2. 2. A POWWAU production
  3. 3. Agenda ★Goal ★ Principles ★ Tips ★Q&A
  4. 4. Goal
  5. 5. Write code that can be used as many times & in as many projects as possible.
  6. 6. Principles
  7. 7. SOLID ★ Single Responsibility ★Open/Closed ★ Liskov Substitution ★ Interface Segregation ★Dependency Inversion
  8. 8. Tips
  9. 9. Forgive me, because I am going to sin ★ The following are tips, not rules. ★ They might not apply to your code. ★But I would, at least, think about them.
  10. 10. 1.
  11. 11. Don’t write reusable code
  12. 12. Don’t write start with reusable code
  13. 13. Don’t start with reusable code ★ Start with the right idea ★But don’t need the perfect code ★ You can always refactor ★ Shipped is better than perfect
  14. 14. 2.
  15. 15. But don’t commit code that is clearly not reusable
  16. 16. Commit reusable code ★Do the Right Thing™ before committing to the development branch or any shared branches. ★Be a good boy scout: If you revisit the code and it can be improved, do so.
  17. 17. 3.
  18. 18. Move all the business logic to the model
  19. 19. Move to the model ★Model > Data ★Business rules • Behaviors • Validation • Importing & exporting • Migration
  20. 20. Instead of - (IBAction) importPasteText:(id)sender { UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; if ([pasteboard containsPasteboardTypes:@[@"public.utf8-plain-text", @"public.text"]]) { NSInteger rejectedItems = 0; CheckItem *newItem = nil; [self.undoManager beginUndoGrouping]; NSString *textToImport = [pasteboard.string copy]; NSArray *items = [textToImport componentsSeparatedByString:@"n"]; NSUInteger objectNumber = 0; if ([[self.itemsTableViewDataSource.fetchedResultsController sections] count] > 0) { id <NSFetchedResultsSectionInfo> sectionInfo = [self.itemsTableViewDataSource.fetchedResultsController sections][0]; objectNumber = [sectionInfo numberOfObjects]; } for (NSString *item in items) { NSArray *itemComponents = [item componentsSeparatedByString:@":"]; NSString *itemName = [itemComponents[0] stringByTrimmingCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]]; if ([itemName length] > 0) { …
  21. 21. do - (IBAction) importPasteText:(id)sender { UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; if ([pasteboard containsPasteboardTypes:@[@“public.utf8-plain-text", @"public.text"]]) { [self.undoManager beginUndoGrouping]; [self.model importItemsInChecklist: self.checklist fromString:pasteboard.string]; [self.undoManager endUndoGrouping]; } }
  22. 22. 4.
  23. 23. Don’t put presentation logic into the model
  24. 24. Presentation logic ★ It isn’t business logic, but “UI logic” ★ In the VC, it could be inconsistent, better “centralized”. ★ It shouldn’t be in the model. ★ It can contain UI dependent information (colors, fonts,…).
  25. 25. Instead of // Set label to “3 days left” - (void) displayRemainingTime { self.remainingLabel.text = [self.task remainingTime]; }
  26. 26. do // Set label to “3 days left” - (void) displayRemainingTime { TaskPresenter *tp = [[TaskPresenter alloc] initWithTask:self.task]; self.remainingLabel.text = [tp remainingTime]; }
  27. 27. 5.
  28. 28. Put the network closer to the model (instead of View Controller)
  29. 29. Network code ★ The view controller is responsible for responding events: • lifecycle • UI generated ★Controlling networking is OK ★ Doing networking is wrong ★ In the model or a category: • Provide business logic with network functionality • Handle concurrency
  30. 30. 6.
  31. 31. Make classes for Data Sources & Delegates of table views
  32. 32. Data Source & Delegate Objects ★ It exceeds VC responsibilities ★Responsibility: View model ★ They can be reused
  33. 33. Instead of // @interface MyVC: UITableViewController @implementation MyVC { //implicit (in super) // self.tableView.dataSource = self; // self.tableView.delegate = self; }
  34. 34. do // @interface MyVC: UITableViewController @interface MyVC @property (strong) MyDataSrc *myDataSrc; @end @implementation MyVC - (void) viewDidLoad { [self assignDataSource]; } - (void) assignDataSource { self.myDataSrc = [[MyDataSrc alloc] initWithMOC:self.moc]; self.myDataSrc.fetchedResourceController.delegate.self; self.tableView.dataSource = self; [self.myDataSource performFetch]; } @end
  35. 35. 7.
  36. 36. But then, don’t let them take care of the views
  37. 37. DataSource & Delegate responsibilities ★Metadata: How many rows, sections… ★ Act on data: add or delete row ★ Provide views, view info and present data ★ Act on selections
  38. 38. Use a delegate numberOfSectionInTableView: tableView:numberOfRowsInSection: tableView:cellForRowAtIndexPath: MyVC MyDataSrc displayObject:inCell
  39. 39. TableViewViewModelDele gate @protocol TableViewViewModelDelegate <NSObject> - (NSString *) cellIdentifier; - (CGFloat) cellHeight; - (UITableViewCell *) displayObject:(NSManagedObject *)object inCell:(UITableViewCell *)cell; - (NSString *) headerIdentifier; - (CGFloat) headerHeight; - (UITableViewHeaderFooterView *) displayObject:(NSManagedObject *)object inHeader: (UITableViewHeaderFooterView *)header; @optional - (void) didSelectObject:(NSManagedObject *)object; - (void) didSelectAccessoryForObject:(NSManagedObject *)object; @end
  40. 40. 8.
  41. 41. Test your code
  42. 42. Test ★Reusable code = code + tests ★ Tests contain • Validation • Assumptions • Documentation
  43. 43. Test your Core Data model @implementation CoreDataTestCase - (void) setUp { [super setUp]; [self createCoreDataStack]; } - (void) createCoreDataStack { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; model = [NSManagedObjectModel mergedModelFromBundles:@[bundle]]; coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; store = [coordinator addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; context.persistentStoreCoordinator = coordinator; }
  44. 44. Test your view controllers - (void) setUp { [super setUp]; [self createFixture]; [self createSut]; } - (void) createSut { UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil]; sut = [storyboard instantiateViewControllerWithIdentifier:viewControllerID]; sut.data = data; }
  45. 45. Test your view controllers (2) - (void) testOnViewDidLoadTitleIsSetToChecklistName { [sut view]; XCTAssertTrue([sut.title isEqualToString:sut.checklist.name], @"View controller title must be set to the checklist name"); }
  46. 46. 9.
  47. 47. Break up your Storyboards into workflows
  48. 48. Huge Storyboards Something to avoid
  49. 49. Workflows ★Huge Storyboards: • Teams get conflicts in the Storyboards often • Difficult to maintain ★Better Storyboards: • One per workflow (sign in/sign up, statistics, preferences) • Even combine with XIB (useful for cells)
  50. 50. Load another Storyboard - (IBAction) displayReport:(id)sender { UIViewController *reportVC = [[UIStoryboard storyboardWithName:reportStoryboard bundle:nil] instantiateInitialViewController]; [self presentViewController:reportVC animated:YES completion:nil]; }
  51. 51. 10.
  52. 52. Create your own modules
  53. 53. Modular code ★ iOS support • Until iOS 8: only static libraries (no resources, separated headers) • iOS 8: frameworks ★Cocoapods: allow to share open source code
  54. 54. Create a pod ★ Isolate the code (use pod-template) ★Create .podspec & LICENSE ★ pod lib create coolpod ★ In your Podfile: • pod ‘coolpod', :git => 'https:// server/repoURL/coolpod.git'
  55. 55. Thank you! Images courtesy of Shutterstock

×