As an exercise, I wrote a simple iOS scene transition from a login form (root view controller) to a success scene. I was experimenting with the Model-View-Presenter approach where view and presenter know each other, but communicate through protocols, so you program to an interface, not the implementation. I ended up using a simple state machine to represent the view’s state, and employed form validation that is represented in the types. I like how the result reads, so here’s a rundown of the exercise:
They moved to a Flux-inspired “unidirectional flow” approach where state changes are performed in a Store (in the backend, if you will) and updates pushed to the user interface (the frontend). No ad-hoc view updates, no shortcuts. User events are emitted, handled by the store, and state updates affect the view. That’s it. So it’s always obvious how the user interface got into the state it’s in: all the details are plainly visible in the current state object.
Ben’s post is very deep and detailed. You’ll learn a ton from it:
architectural discussion: why “unidirection flow”, and why Flux?
setup of actions/events and the store type
basic state–view bindings via Reactive Cocoa
how testing stores (and views) is straightforward with this setup
I can’t wait for the next post in this series. Most of this easily applies to ReSwift, too, of course. And guess who just got inspired to refactor TableFlip so the model and view become even more loosely coupled?
Krzysztof Zabłocki wrote about the concept of “Flow Controllers”. Assimilating the concept, I imagine they’re similar to the bootstrappers I use during app launch, only Krzysztof says there’s just one initial flow controller after launch while the others are used when transitions need to take place.
Separating state from transitions is a higher-level goal of architecting apps – and is especially amiss in UIViewController-centered iOS apps. “Flow Controllers” encapsulate the change or transition. This includes setting up the view controller according to the presentation needs:
Configuring view controller for specific context - e.g. different configuration for an Image Picker shown from application Create Post screen and different when changing user avatar
Listening to important events on each ViewController and using that to coordinate flow between them.
Providing view controller with objects it needs to fulfill it’s role, thus removing any need for singletons in VC’s
The singleton Krzysztof talks about is the usual way app state is modelled.
Here’s a slightly modified example to illustrate how a flow controller manages transitions between view controller scenes with configureProgramsViewController, configureCreateProgramViewController, or similar:
funcconfigureProgramsViewController(viewController:ProgramsViewController,navigationController:UINavigationController){viewController.state=state// Add button action callback to show the next sceneviewController.addProgram={[weakself]_inguardletstrongSelf=selfelse{return}letcreateVC=// recreate a view controller, e.g. from a StoryboardstrongSelf.configureCreateProgramViewController(createVC,navigationController:navigationController)navigationController.pushViewController(createVC,animated:true)}}
The flow controller owns the state here and takes care of pushing view controllers onto the navigation stack. This will not work with segues. It’s a replacement for them.
Benjamin Encz’s presentation “Unidirectional Data Flow in Swift” about ReSwift features global app state: there’s one AppState type that acts as the facade to model and navigation state which is the single point of truth of every state in the app. This is a game changer when you suffer from massive view controller syndrome. In this post, I’d like to show you how he envisions the state of an app and what a next step could look like.
I think I found something better than closures to handle events. Instead of dispatching closures that are themselves the changes that should be performed, simply declare the change.
enumAction{caseResizeBanana(BananaId,Size)}
Now you need some kind of action handler, which Benjamin Encz calls “reducer” in his presentation “Unidirectional Data Flow in Swift”. Think of a list of reducers as a Chain of Responsibility only without the final consummation of the action. It is passed throughout the whole responder chain. Why reducer? Because they operate similar to the reduce function. The end result will be the combined result of all reducer’s mutations.
This is cool, because no component needs to know what such an action entails – none except the appropriate reducers.
If closures were great because where you type them you can see what is going on (more like “how” in my opinion), a simple action enum is even simpler, telling what should happen.
Works well with CQRS, I imagine. Will have to write a demo app using CQRS sometime to validate.
The current project is called ReSwift and code is available on GitHub.
Reflecting on a recent change of the Word Counter’s file monitoring module, I think I re-discovered a commonly advised pattern in my code: to separate state from state changes. There’s an object that knows how to handle files based on extension: plain text files’s words are counted differently than Word or Scrivener files. Call it Registry. Previously, this was set up once and didn’t change. Now I wanted to make this configurable so users can add custom plain text extensions. This means changing that object’s state.