Using didSet and enums to manage the view state
Many iOS developers come from other platforms and don't explore some features that swift has. People often translate code from one language to another and it's fine if it just works.
Swift has awesome built-in features and I want to show how you can use two of them to manage your view's state in a more declarative way.
The first thing I want to talk about is how you can observe and respond to changes on a property. You can respond to changes in a class property by defining two observers: willSet and didSet. The first will be called before the property setter and the other one after.
Another interesting feature is the ability to add an associated value to an enum case. We can send some dynamic data alongside the case value.
Back to our main goal
The idea is to use these two features to make a state handler that updates our view. First things first, let's map our view state using an enum.
I'm also gonna define two protocols to define how the view and our external module should work.
Each case represents a state of our view. Imagine the user clicks a button, that would fire the loading state, later if something goes wrong the error state is set and if everything runs fine our success case with our data(in our case name) would be the current state.
So, ViewProtocol says that the view should have a state property and ModuleProtocol needs a view that conforms to ViewProtocol and a method for searching a name (the shouldThrowError is just for tests you probably wouldn't have something like that on a method of your own).
Let's make the ViewController conform to ViewProtocol:
Now, let's write a test class that conforms to ModuleProtocol to simulate a network behavior and change the view's state:
If shouldThrowError is true, then set State.error as the view's state.
Before making any request or async work on the background, the state should be State.loading and if everything goes right, set the state to State.success passing the data as an associated value.
I just simulated a long-running task so the loading state is visible for 2 seconds.
The last thing to do is to implement the didSet on the ViewController's state:
If .error is set: show the error label and don't show any data. If .loading is the state: don’t show the error label and show some text indicating that our app is loading data. Finally, in the .success case, show the data.
To make this work we just have to instantiate the module and call that method (change the parameter to see the changes):
And that's it!
Conclusion
What I like about this approach is that: since the state is now a property, it's easy to know in which state you are at any point of the app and not just when the user triggers an action that changes something on the screen.
And the state property can be improved. You can define an array of states, push a state everytime something changes, and have a sort of history of what the user did on that screen.
Sample App
I made a sample project to show how the view changes as the state property is changed. You can interact with a UISwitch and hit a UIButton to fire the module searchForName call.
