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

Clean Architecture - VIPER

1bce3060764fb0f9a5f3f8e556d3b2c6?s=47 Sergi Gracia
November 13, 2014

Clean Architecture - VIPER

The Redbooth iOS Team is adopting a new architecture: VIPER. We explain what's viper and our experience with it (demo included).

1bce3060764fb0f9a5f3f8e556d3b2c6?s=128

Sergi Gracia

November 13, 2014
Tweet

Transcript

  1. WELCOME TO REDBOOTH (there are ! and snacks, find them!)

    TWEET ABOUT THE TALK WITH THE #VIPERTALK HASHTAG
  2. REDBOOTH.COM Try it now!

  3. 2008 TEAMBOX ‑ 2014 REDBOOTH

  4. None
  5. 4

  6. 20

  7. 35

  8. 13

  9. HOW IS LIFE AT REDBOOTH?

  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. REDBOOTH MOBILE TEAM

  20. None
  21. VIPER LOOKING FOR THE PERFECT ARCHITECTURE ! @PEPIBUMUR / @SERGIGRACIA

    / @SAKY
  22. INDEX ▸ ViewControllers ▸ VIPER ▸ Communication ▸ Testing ▸

    Conclusions
  23. INDEX ▸ ViewControllers ▸ VIPER ▸ Communication ▸ Testing ▸

    Conclusions
  24. DOCUMENTATION, BOOKS, EXAMPLES, TUTORIALS objc.io, RayWenderlich, iOS Dev Weekly, NSHipster

  25. PATTERNS PROTOCOLS, DELEGATES, DATA SOURCES KVO

  26. None
  27. Remember THEY ARE EXAMPLES

  28. VIEWCONTROLLER

  29. A VIEW CONTROLLER COORDINATES ITS EFFORTS WITH MODEL OBJECTS AND

    OTHER CONTROLLER OBJECTS—INCLUDING OTHER VIEW CONTROLLERS — Apple
  30. ▸ ViewControllerDelegator @interface TBThreadDetailViewController : RBViewController <UITableViewDataSource, UITableViewDelegate, RBViewControllerURLProtocol, NSFetchedResultsControllerDelegate,

    TBObjectDetailHeaderCellDelegate, TBUploadCellDelegate,TBWatchersViewDelegate> !
  31. ▸ ViewControllerDelegator @interface TBThreadDetailViewController : RBViewController <UITableViewDataSource, UITableViewDelegate, RBViewControllerURLProtocol, NSFetchedResultsControllerDelegate,

    TBObjectDetailHeaderCellDelegate, TBUploadCellDelegate,TBWatchersViewDelegate> ! ▸ Multi Responsibility [self presentViewController:previewController animated:YES completion:nil]; //Navigation [Task downloadNewObjectWithID:self.threadIdentifier.remoteIdentifier withSuccess:^{}]; // Networking NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:[Comment entityName]]; // Data persistence [self.view setBackgroundColor:ss_Color_White]; // Formatting !
  32. ▸ Monster files size > 1500 lines !

  33. ▸ Monster files size > 1500 lines ! ▸ Impossible

    to test "
  34. ▸ Monster files size > 1500 lines ! ▸ Impossible

    to test " ▸ Components too coupled #
  35. ▸ Monster files size > 1500 lines ! ▸ Impossible

    to test " ▸ Components too coupled # ▸ Hard to understand (and review) classes. $ %&'()*+,
  36. None
  37. None
  38. CLEAN ARCHITECTURE INDEPENDENT FROM FRAMEWORKS, UI, DATABASE AND EXTERNAL ENTITIES.

    EASY TO TEST
  39. ▸ Who will persist the data?

  40. ▸ Who will persist the data? ▸ And the API

    interaction?
  41. ▸ Who will persist the data? ▸ And the API

    interaction? ▸ Who controls the navigation?
  42. Viper

  43. None
  44. INDEX ▸ ViewControllers ▸ VIPER ▸ Communication ▸ Testing ▸

    Conclusions
  45. VIPER VIEW, INTERACTOR, PRESENTER, ENTITY, AND ROUTING OBC.IO - VIPER

  46. WHY VIPER ❤?

  47. None
  48. None
  49. None
  50. VIEW ▸ Shows the content received from the presenter ▸

    Notifies user's actions to the presenter ▸ The presenter doesn't know anything about UI
  51. None
  52. PRESENTER ▸ Includes the logic to format the view ▸

    Gets the data from the interactor ▸ Receives actions from the view and traduces them into: Navigation actions (wireframe) and Interactor requests
  53. None
  54. INTERACTOR ▸ Associated to an unique use case of the

    application ▸ Works with PONSO entities ▸ Coordinates both data managers
  55. None
  56. DATAMANAGER ▸ Provider of entities for the Interactor ▸ Responsible

    of the persistence (Web and Local) ▸ The entities don't know about how to persist themselves
  57. None
  58. WIREFRAME ▸ Initializes the VIPER module ▸ It knows how

    to navigate ▸ Delegate of transitions animations
  59. KEEP IN MIND

  60. PROTOCOLS DEPENDENCY INVERSION INTERFACE SEGREGATION (SOLID)

  61. STRONG/WEAK BE CAREFUL WITH RETAIN CYCLES ➿ @interface TweetDetailViewController: UIViewController

    @property (nonatomic, strong) id <TweetDetailPresenterInput> presenter; @end @interface TweetDetailPresenter: NSObject<TweetDetailPresenterInput> @property (nonatomic, weak) id <TweetDetailViewInput> view; @end @implementation TweetDetailPresenter - (void)sendTweet:(NSString*)tweet { __weak typeof(self) welf = self; [self.view showLoader]; [self.interactor sendTweetWithCompletion:^(NSError *error) { [welf.view hideLoader]; if (!error) [welf.wireframe moveBack]; }]; } @end
  62. ENTITIES DON'T PASS NSMANAGEDOBJECTS! USE PONSOS INSTEAD @interface TweetEntity: NSObject

    @property (nonatomic, strong) NSString *body; @property (nonatomic, strong) NSString *authorName; @property (nonatomic, strong) NSDate *creationDate; + (TweetEntity*)tweetEntityFromTweet:(Tweet*)tweet; @end
  63. None
  64. INDEX ▸ ViewControllers ▸ VIPER ▸ Communication ▸ Testing ▸

    Conclusions
  65. INNER COMMUNICATION

  66. None
  67. OUTER COMMUNICATION

  68. None
  69. None
  70. WAIT...

  71. TESTING

  72. None
  73. INDEX ▸ ViewControllers ▸ VIPER ▸ Communication ▸ Testing ▸

    Conclusions
  74. TESTING VIEW

  75. TESTING PRESENTER

  76. TESTING INTERACTOR

  77. TESTING DATAMANAGER

  78. Demo

  79. TWITTER APP Login and Home views WRITTEN 100% IN SWIFT

    GITHUB.COM/PEPIBUMUR/VIPER-MODULE-GENERATOR HANEKE, SUGARRECORD, SWIFTER, PURELAYOUT, PROGRESSHUD
  80. LOGIN FLOW

  81. ▸ The VIPER module is initialized and presented by the

    Wireframe ▸ The view notifies that DidLoad to the Presenter override func viewDidLoad() { self.setupSubviews() self.setupConstraints() self.setNeedsStatusBarAppearanceUpdate() self.presenter?.viewDidLoad() }
  82. ▸ The Presenter formats the View's content func viewDidLoad() {

    self.view?.setLoginTitle("Login Twitter") self.view?.setLogo(UIImage(named: "twitter_logo")!) }
  83. When the user taps on Login ▸ The View notifies

    the Presenter func userDidSelectLogin(sender: AnyObject) { self.presenter?.userDidSelectLogin() }
  84. The Presenter: ▸ Tells the View to show a loader

    ▸ Asks the Interactor for Login func userDidSelectLogin() { self.view?.showLoader() self.interactor?.login() { [weak self] (error: NSError?) -> () in if error != nil { // What should we do here? } else { self?.view?.hideLoader() // And here? } } }
  85. The Interactor: ▸ Login the user through the APIDataManager ▸

    Persists the user's credentials using the LocalDataManager
  86. func login(completion: (error: NSError?) -> ()) { self.APIDataManager?.login({ [weak self]

    (error: NSError?, credentials: TwitterLoginItem?) -> () in if (credentials != nil) { self?.localDatamanager?.persistUserCredentials(credentials: credentials!) completion(error: nil) } else { completion(error: error) } }) }
  87. APIDATAMANAGER func login(completion: (error: NSError?, loginItem: TwitterLoginItem?) -> ()) {

    TwitterClient.requestAccesss { (error, credentials) -> () in if credentials != nil { completion(error: nil, loginItem: TwitterLoginItem(swifterCredentials: credentials!)) } else { completion(error: error, loginItem: nil) } } }
  88. LOCALDATAMANAGER func persistUserCredentials(#credentials: TwitterLoginItem) { TwitterAccountManager.persistAccount(fromLoginItem: credentials) }

  89. If the login fails ▸ The Presenter asks the View

    to show an error func showError(let errorMessage: String) { ProgressHUD.showError(errorMessage) } If the login success ▸ The Presenter asks the Wireframe to show the home view
  90. Demo

  91. INDEX ▸ ViewControllers ▸ VIPER ▸ Communication ▸ Testing ▸

    Conclusions
  92. SOME CONCLUSIONS ▸ Lighter, more specific and readable classes ▸

    Each team member can be working on a different component once the interfaces are defined ▸ There're no excuses for TDD !
  93. TIPS ▸ Heavy work but you and you'll team will

    thank it ▸ Keep in mind the SOLID principles ▸ Refactor your components through iterations ▸ Decouple your code from the database models and data layers
  94. RESOURCES ▸ VIPER Module Generator ▸ Objc.io post ▸ Mutual

    Mobile Engineering blog post ▸ Dobuts/Ideas/Suggestions on Github issues
  95. None
  96. ✋DOUBTS?

  97. We are hiring!