Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Architecture - VIPER

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).

Sergi Gracia

November 13, 2014
Tweet

More Decks by Sergi Gracia

Other Decks in Programming

Transcript

  1. WELCOME TO
    REDBOOTH
    (there are ! and snacks, find them!)
    TWEET ABOUT THE TALK WITH THE #VIPERTALK HASHTAG

    View Slide

  2. REDBOOTH.COM
    Try it now!

    View Slide

  3. 2008
    TEAMBOX

    2014
    REDBOOTH

    View Slide

  4. View Slide

  5. 4

    View Slide

  6. 20

    View Slide

  7. 35

    View Slide

  8. 13

    View Slide

  9. HOW IS LIFE AT
    REDBOOTH?

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. REDBOOTH MOBILE TEAM

    View Slide

  20. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. PATTERNS
    PROTOCOLS, DELEGATES,
    DATA SOURCES
    KVO

    View Slide

  26. View Slide

  27. Remember
    THEY ARE
    EXAMPLES

    View Slide

  28. VIEWCONTROLLER

    View Slide

  29. A VIEW CONTROLLER COORDINATES ITS EFFORTS
    WITH MODEL OBJECTS AND OTHER CONTROLLER
    OBJECTS—INCLUDING OTHER VIEW CONTROLLERS
    — Apple

    View Slide

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

    View Slide

  31. ▸ ViewControllerDelegator
    @interface TBThreadDetailViewController : RBViewController 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
    !

    View Slide

  32. ▸ Monster files
    size > 1500 lines !

    View Slide

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

    View Slide

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

    View Slide

  35. ▸ Monster files
    size > 1500 lines !
    ▸ Impossible to test "
    ▸ Components too coupled #
    ▸ Hard to understand (and review) classes. $
    %&'()*+,

    View Slide

  36. View Slide

  37. View Slide

  38. CLEAN ARCHITECTURE
    INDEPENDENT FROM FRAMEWORKS, UI,
    DATABASE AND EXTERNAL ENTITIES. EASY
    TO TEST

    View Slide

  39. ▸ Who will persist the data?

    View Slide

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

    View Slide

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

    View Slide

  42. Viper

    View Slide

  43. View Slide

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

    View Slide

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

    View Slide

  46. WHY VIPER ❤?

    View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. VIEW
    ▸ Shows the content received from the presenter
    ▸ Notifies user's actions to the presenter
    ▸ The presenter doesn't know anything about UI

    View Slide

  51. View Slide

  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

    View Slide

  53. View Slide

  54. INTERACTOR
    ▸ Associated to an unique use case of the application
    ▸ Works with PONSO entities
    ▸ Coordinates both data managers

    View Slide

  55. View Slide

  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

    View Slide

  57. View Slide

  58. WIREFRAME
    ▸ Initializes the VIPER module
    ▸ It knows how to navigate
    ▸ Delegate of transitions animations

    View Slide

  59. KEEP IN MIND

    View Slide

  60. PROTOCOLS
    DEPENDENCY INVERSION
    INTERFACE SEGREGATION
    (SOLID)

    View Slide

  61. STRONG/WEAK
    BE CAREFUL WITH RETAIN CYCLES ➿
    @interface TweetDetailViewController: UIViewController
    @property (nonatomic, strong) id presenter;
    @end
    @interface TweetDetailPresenter: NSObject
    @property (nonatomic, weak) id 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

    View Slide

  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

    View Slide

  63. View Slide

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

    View Slide

  65. INNER
    COMMUNICATION

    View Slide

  66. View Slide

  67. OUTER
    COMMUNICATION

    View Slide

  68. View Slide

  69. View Slide

  70. WAIT...

    View Slide

  71. TESTING

    View Slide

  72. View Slide

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

    View Slide

  74. TESTING VIEW

    View Slide

  75. TESTING PRESENTER

    View Slide

  76. TESTING INTERACTOR

    View Slide

  77. TESTING DATAMANAGER

    View Slide

  78. Demo

    View Slide

  79. TWITTER APP
    Login and Home views
    WRITTEN 100% IN SWIFT
    GITHUB.COM/PEPIBUMUR/VIPER-MODULE-GENERATOR
    HANEKE, SUGARRECORD, SWIFTER, PURELAYOUT, PROGRESSHUD

    View Slide

  80. LOGIN FLOW

    View Slide

  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()
    }

    View Slide

  82. ▸ The Presenter formats the View's content
    func viewDidLoad()
    {
    self.view?.setLoginTitle("Login Twitter")
    self.view?.setLogo(UIImage(named: "twitter_logo")!)
    }

    View Slide

  83. When the user taps on Login
    ▸ The View notifies the Presenter
    func userDidSelectLogin(sender: AnyObject)
    {
    self.presenter?.userDidSelectLogin()
    }

    View Slide

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

    View Slide

  85. The Interactor:
    ▸ Login the user through the
    APIDataManager
    ▸ Persists the user's credentials using the
    LocalDataManager

    View Slide

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

    View Slide

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

    View Slide

  88. LOCALDATAMANAGER
    func persistUserCredentials(#credentials: TwitterLoginItem)
    {
    TwitterAccountManager.persistAccount(fromLoginItem: credentials)
    }

    View Slide

  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

    View Slide

  90. Demo

    View Slide

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

    View Slide

  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 !

    View Slide

  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

    View Slide

  94. RESOURCES
    ▸ VIPER Module Generator
    ▸ Objc.io post
    ▸ Mutual Mobile Engineering blog post
    ▸ Dobuts/Ideas/Suggestions on Github issues

    View Slide

  95. View Slide

  96. ✋DOUBTS?

    View Slide

  97. We are hiring!

    View Slide