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

One Way Flow of Data

kathryneh
November 11, 2015

One Way Flow of Data

At Square's Women Engineering Lightning Talk event, #hearandnow, Square engineer Kat Hawthorne presents an alternative iOS application architecture that her team designed to build a new version of Square Register.

kathryneh

November 11, 2015
Tweet

Transcript

  1. RETHINKING IOS APP ARCHITECTURE USING A ONE WAY DATA FLOW

    kat hawthorne, software engineer @square #hearandnow | @khhawthorne
  2. VIEW CONTROLLERS OUT OF CONTROL SHARED DATA CAUSES HARD TO

    DISCOVER BUGS CODE WITH TOO MUCH RESPONSIBILITY IS HARD TO TEST COMMON PROBLEMS IN IOS APPLICATIONS #hearandnow | @khhawthorne
  3. VIEW CONTROLLERS HAVE TOO MUCH RESPONSIBILITY SHARED DATA CAUSES HARD

    TO DISCOVER BUGS CODE WITH TOO MUCH RESPONSIBILITY IS HARD TO TEST #hearandnow | @khhawthorne
  4. SHIRT $20 CHECKOUT 1 ITEM FOR $21 TAX $1 TOTAL

    $21 CART #hearandnow | @khhawthorne
  5. SHIRT $20 CHECKOUT 1 ITEM FOR $21 TAX $1 TOTAL

    $21 [JIRA] (LST-99) CHECKOUT BUTTON COLOR DOES NOT UPDATE CART #hearandnow | @khhawthorne
  6. VIEW CONTROLLERS HAVE TOO MUCH RESPONSIBILITY SHARED DATA CAUSES HARD

    TO DISCOVER BUGS CODE WITH TOO MUCH RESPONSIBILITY IS HARD TO TEST #hearandnow | @khhawthorne
  7. SHIRT $20 CART CALCULATE TOTAL HAT $20 X [JIRA] (LST-100)

    APP CRASHES WHEN CALCULATING TOTAL AND DELETING ITEM SIMULTANEOUSLY* #hearandnow | @khhawthorne
  8. VIEW CONTROLLERS HAVE TOO MUCH RESPONSIBILITY SHARED DATA CAUSES HARD

    TO DISCOVER BUGS CODE WITH TOO MUCH RESPONSIBILITY IS HARD TO TEST #hearandnow | @khhawthorne
  9. EMPTY CART CART WITH ITEMS ERROR STATE SUCCESS STATE VIEW

    CONTROLLER TESTS #hearandnow | @khhawthorne
  10. EMPTY CART CART WITH ITEMS ERROR STATE SUCCESS STATE ERROR

    STATE SUCCESS STATE VIEW CONTROLLER TESTS #hearandnow | @khhawthorne
  11. EMPTY CART CART WITH ITEMS ERROR STATE SUCCESS STATE ERROR

    STATE SUCCESS STATE AND SO ON… VIEW CONTROLLER TESTS #hearandnow | @khhawthorne
  12. ONE WAY FLOW OF DATA: THE CYCLE RENDERER PRESENTER VIEW

    CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT #hearandnow | @khhawthorne
  13. ONE WAY FLOW OF DATA: PUTTING IT TOGETHER RENDERER PRESENTER

    VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT #hearandnow | @khhawthorne
  14. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    RENDERER class Renderer { func render() { let cartViewModel = CartPresenter.present( accountService: context.accountService, cartService: context.cartService, errorService: context.errorService, itemService: context.itemService ) cartViewController.update(cartViewModel) } } #hearandnow | @khhawthorne
  15. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    CONTEXT class AppContext { private(set) lazy var cartService: CartService = CartService( cartState: self.cartState, catalogService: self.catalogService, accountService: self.accountService, render: self.render ) } #hearandnow | @khhawthorne
  16. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    PRESENTER final class CartPresenter { class func present( accountService: AccountService, cartService: CartService, errorService: ErrorService, itemService: ItemService ) -> CartViewModel? {…} } #hearandnow | @khhawthorne
  17. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    VIEW MODEL struct CartViewModel { let preSubtotalLineItems: [LineItem] let emptyCartMessage: String? let displayTotal: String? … } #hearandnow | @khhawthorne
  18. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    VIEW CONTROLLER final class CartViewController : UIViewController { private(set) cartView: CartView = CartView() func update(viewModel: CartViewModel?){ cartView.emptyCartMessage = viewModel.emptyCartMessage cartView.displayTotalLabel = viewModel.displayTotal … } } #hearandnow | @khhawthorne
  19. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    VIEW final class CartView : UIView { let emptyCartMessage: UILabel let displayTotalLabel: UILabel … } #hearandnow | @khhawthorne
  20. RENDERER PRESENTER VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT

    SERVICE class CartService { func addSKU(sku: String) { let item = catalog.getItem(sku: SKU) let cart = self.cart.mutableCopy() cart.mutableLineItems.addItemization(item) self.cart = cart.copy() render() } } #hearandnow | @khhawthorne
  21. ONE WAY FLOW OF DATA: PUTTING IT TOGETHER RENDERER PRESENTER

    VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT #hearandnow | @khhawthorne
  22. VIEW CONTROLLERS OUT OF CONTROL SHARED DATA CAUSES HARD TO

    DISCOVER BUGS CODE WITH TOO MUCH RESPONSIBILITY IS HARD TO TEST COMMON PROBLEMS IN IOS APPLICATIONS #hearandnow | @khhawthorne
  23. MULTI-LAYERED ARCHITECTURE SINGLE RESPONSIBILITY PRINCIPLE VIEW CONTROLLERS HAVE TOO MUCH

    RESPONSIBILITY LESS CODE = LESS MENTAL OVERHEAD #hearandnow | @khhawthorne
  24. MINIMAL STATE IN THE APP CLEAR DELINEATION OF WHAT CAN

    MODIFY STATE SHARED DATA CAUSES HARD TO DISCOVER BUGS ONE WAY FLOW OF DATA ELIMINATES UNEXPECTED BEHAVIOR #hearandnow | @khhawthorne
  25. THIN APPLICATION LAYERS DEPENDENCY INJECTION RATHER THAN RELYING ON SHARED

    STATE CODE WITH TOO MUCH RESPONSIBILITY IS HARD TO TEST #hearandnow | @khhawthorne
  26. ONE WAY FLOW OF DATA: PUTTING IT TOGETHER RENDERER PRESENTER

    VIEW CONTROLLER VIEW SERVICE REPOSITORY VIEW MODEL CONTEXT #hearandnow | @khhawthorne