State Machine with ViewModel

Swati Wadhera
The Startup
Published in
3 min readMay 3, 2020

In every application be it small or big, there might be asynchronous loading of contents for a screen or may be in one of its components. As we know, asynchronous calls may have a fast response time or slow response time which is dependent on various factors such as network connection, api response time, device, etc. Obviously, we would not want our users to see a blank screen while that asynchronous calls is doing its job.

Developers tend to implement this by hiding the content view which might be table view, collection view or something else and adding activity indicator as its subview. Once the data is fetched, this activity indicator is removed from the view and content view is unhidden. Also, in case of error this activity indicator is removed and another view is added with a label containing the error message.

Although, this approach is simple but its not scalable with large applications and handling different operations on success or failure of data.

I have implemented MVVM architecture in my application which took advantage of ViewModel in communicating with ViewController to show success or error states. My ViewModel informs ViewController about the current state of my view and hence, it updates itself accordingly.

Lets get started with the code now.

I have created an enum with different states that a view can hold. The error state will hold a description with the text to be shown in the view in case of failure.

enum ViewState {
case loading
case success
case error(description: String)
}

Lets create a protocol which our ViewController will conform and implement all its methods

protocol StateProtocol: class {
func loading()
func success()
func error(description: String)
}

Our View model will create a delegate object of StateProtocol which will be implemented by ViewController.

class MyViewModel {
weak var delegate: StateProtocol?

init(delegate: StateProtocol) {
self.delegate = delegate
}
}

Our View Controller will create an object of this viewmodel and set its delegate to self and will also implement its methods.

class MyViewController: ViewController {
lazy var viewModel = MyViewModel(delegate: self)
}
extension MyViewController: StateProtocol {
func loading() {
// show loader
}

func success() {
// handle success
}
func error(description: String) {
//
handle error
}
}

Now, lets create a dummy function in our view m odel which will call some api in universe asynchronously.

class MyViewModel {
weak var delegate: StateProtocol?

init(delegate: StateProtocol) {
self.delegate = delegate
}
func callSomeApi() {
apiManager.callSomeApi(completion: {

}, failure: {
})
}
}

How do I inform my view controller about the latest state now. There are two approaches.

Approach 1

func callSomeApi() {
apiManager.callSomeApi(completion: {
delegate?.success()
}, failure: {
delegate?.error(decription: "Could not fetch data")
})
}

Approach 2

This approach consists of defining a variable holding the current state. I prefer this approach in case I need to access the current state at a later stage and calling delegate function with same state from different apis also makes this handy.

var state: State = .initial {
didSet {
switch state {
case .loading:
delegate?.loading()
case .success:
delegate?.success()
case .error(let error):
delegate?.error(msg: error)
}
}
}
func callSomeApi() {
apiManager.callSomeApi(completion: {
state?.success()
}, failure: {
delegate?.error(decription: "Could not fetch data")
})
}

My view controller will conform to this protocol and handle the loader, error and success as per the information transferred by view model. We can also call multiple apis which may or may not need view controller to show loader, success or failure which will be handled smoothly with this approach.

Conclusion

There might be multiple ways of handling success, failures in view controller and every developer has their own convenient way of handling this. I have just created another approach which is best suited with complex apps and built upon MVVM Architecture.

I will share an approach for showing loader with customised text, failure and try again button in another article.

Thanks for reading 🙌🏼

--

--