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

try! Swift 2017 – View-state driven applications

try! Swift 2017 – View-state driven applications

A look at modeling view state as a separate, serializable model within your app and the functionality, fun and/or serious debugging you can unlock by driving your view state through data rather than presentation, including jumping forwards and backwards in time, replaying your user-interface and being able to log and inspect your view state to ensure program correctness.

Matt Gallagher

September 06, 2017
Tweet

More Decks by Matt Gallagher

Other Decks in Programming

Transcript

  1. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS ONE

    GOAL, THREE QUESTIONS ▸ Goal: think about view-state. ▸ What is view-state? ▸ How is view-state treated in standard Cocoa apps? ▸ What happens if we take this (mostly hidden) data and use it to drive the whole application?
  2. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS THE

    COCOA APPLICATION DESIGN PATTERN ▸ Described as "Model-View-Controller" CONTROLLER VIEW MODEL
  3. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS IDEAL

    MODEL-VIEW-CONTROLLER CHANGE PATTERN VIEW CONTROLLER MODEL OBSERVER CONTROLLER VIEW Runtime binding Compile-time binding Model context
  4. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS REASONS

    FOR DATA-DRIVEN PATTERNS ▸ Clean interface on the model makes testing easier ▸ Views can be coordinated by depending on common state ▸ Reduces the need for knowledge ▸ And because all model changes happen on the same path, we can do interesting things…
  5. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS TWO

    SETS OF RULES ▸ "Document" data – the “model” – uses a data-driven pipeline ▸ "View-state" uses a presentation-driven pipeline
  6. Model context MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN

    APPLICATIONS INSTEAD OF THIS DATA-DRIVEN PATTERN… VIEW CONTROLLER MODEL OBSERVER CONTROLLER VIEW VIEW CONTROLLER CONTROLLER WE HAVE THIS PRESENTATION-DRIVEN PATTERN… VIEW
  7. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS CHANGING

    DETAIL VIEW (PRESENTATION-DRIVEN) override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { performSegue(withIdentifier: "showDetail", sender: self) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetail" { if let indexPath = tableView.indexPathForSelectedRow { let timezone = sortedTimezones[indexPath.row] let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController controller.timezone = timezone } } } override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { Document.shared.removeObject(sortedObjects[indexPath.row]) } } @objc func handleChangeNotification(_ notification: Notification) { sortedObjects = Document.shared.objectsSortedByKey(sortKey) tableView.reloadData() } DELETING A ROW (DATA-DRIVEN)
  8. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS WHAT

    ARE THE CONSEQUENCES? ▸ Total coupling between cause and effect ▸ Difficult to add additional dependencies (each additional dependency requires manual propagation) ▸ Controllers need to inter-communicate to send segue data ▸ No clean interface for testing or other hooks
  9. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS DOUBLE

    STANDARD ▸ There are good reasons why we use a “data driven” approach for model data. ▸ Why do we fail to apply these same techniques to view- state? Is view-state really any different? ▸ Both are critical to our apps. Both should be tested. Both should be persisted.
  10. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS EVERYTHING

    MUTABLE IN YOUR APP IS A MODEL ▸ View-state is a model, changes to view-state should take a data-driven path. ▸ Let's identify the view-state in the app and make it into its own model.
  11. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS LET'S

    LOOK AT THE DATA MODEL class Document: NotifyingStore { let url: URL private (set) var timezones: [UUID: Timezone] = [:] } struct Timezone: Codable { var name: String let identifier: String let uuid: UUID } ▸ The most important consideration is: what is mutable?
  12. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS A

    REPRESENTATION OF VIEW-STATE struct SplitViewState: Codable { var masterView: MasterViewState var detailView: DetailViewState? var selectionView: SelectionViewState? } struct MasterViewState: Codable { var masterScrollOffsetY: Double = 0 var isEditing: Bool = false } struct DetailViewState: Codable { let uuid: UUID } struct SelectionViewState: Codable { var selectionScrollOffsetY: Double = 0 var searchText: String = "" } ▸ Combined with the 10 lines of data model, we now have a 30 line abstract representation of the whole 600 line app.
  13. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS VIEW-STATE

    DRIVEN (DATA-DRIVEN) override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { ViewState.shared.setDetailSelection(uuid: sortedTimezones[indexPath.row].uuid) } @objc func handleViewStateNotification(_ notification: Notification) { let detailView = ViewState.shared.topLevel.detailView if let uuid = detailView?.uuid, Document.shared.timezones[uuid] != nil, lastPresentedUuid != uuid { lastPresentedUuid = uuid performSegue(withIdentifier: notification.userActionData != nil ? "detail" : "detailWithoutAnimation", sender: self) } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { performSegue(withIdentifier: "showDetail", sender: self) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetail" { if let indexPath = tableView.indexPathForSelectedRow { let timezone = sortedTimezones[indexPath.row] let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController controller.timezone = timezone } } } CHANGING DETAIL VIEW (PRESENTATION-DRIVEN)
  14. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS TIME

    TRAVEL ▸ Time travel is common in "unidirectional data flow" approaches like ReSwift but those architectures involve radical changes to the whole app. ▸ A view-state driven approach doesn’t require a radical rethink of architecture, it just involves treating more of your app like the mode. ▸ The Clocks sample app changes very little from Apple’s Master-Detail app template.
  15. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS TESTABILITY

    ▸ User-interfaces don't provide a good code interface to test. Models are abstract, can be isolated and tested. ▸ I don’t just mean “unit testing”, I mean logging application state: {“selectionView":{"selectionScrollOffsetY": 1669,"searchText":"Melbourne"},"masterView":{"masterScrollOffsetY": 688,"isEditing":false}} ▸ This lets us answer the question of “what just happened”? ▸ It also lets us recreate what just happened so we can fix the problem. ▸ Transitions between two arbitrary states also becomes simple.
  16. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS VIEW-STATE

    DATA-DRIVEN CHANGE PATTERN VIEW CONTROLLER VIEW-STATE MODEL OBSERVER CONTROLLER VIEW Runtime binding Compile-time binding Model context
  17. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS DRAWBACKS

    ▸ Slightly more code because everything needs a change pipeline rather than direct coupling. ▸ Most built-in view controllers need a little cajoling to give up their view state. ▸ Clocks app shows an approach for navigation stack, split views, scroll positions.
  18. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS HOW

    DID WE GO? ▸ Goal was to think about view-state. ▸ View-state is anything mutable in a view that isn’t written to the normal “document” model. ▸ Standard Cocoa apps treat it as a side-effect of presentation. ▸ We can treat presentation as a consequence of view-state.
  19. MATT GALLAGHER, TRY! SWIFT NYC 2017: VIEW-STATE DRIVEN APPLICATIONS VIEW

    THE "CLOCKS" SAMPLE APP ▸ https://github.com/mattgallagher/Clocks FIND MORE ▸ Web: https://www.cocoawithlove.com ▸ Twitter: @cocoawithlove