Using Enums and Closures to Substitute Delegates
As iOS developers, we are used to using delegates to move data asynchronously, and Apple sure loves to use them as well.
However, if you are like me (and many other iOS developers), avoiding using them is a significant part of our development steps, especially when using MVVM without RxSwift (or any other functional reactive programming library).
We are going to use closures in combination with enums to avoid using delegates between the ViewModel
and the ViewController
.
The project that we’ll work on uses delegates, and we will refactor it to use enums and closures instead.
If you are not familiar enough with closures, I recommend you take a look at this one first: Don’t abuse Delegates, Use Swift Closures Instead
First, visit here to get the initial project.
Let’s run the app to get an idea of how it works:
Is a straightforward app, it has an Api Client, a View Model, and one View Controller.
The Api Client makes the network call to get all the jobs, then sends the objects to the View Model and the View Model notify the View Controller about the loading state and passes the objects to be shown.
All these calls are asynchronous. Therefore we are using delegates to pass the data from the Api Client all the way to the View Controller.
Refactoring:
Api Client
Here we have two delegates:
Let’s begin creating an enum
inside the ApiClient
class called Response
.
Now let’s create a typealias
for our closure statement.
We are almost done, now let’s modify the function signature to have our just declared Closure
typealias.
Great, now lets put all the pieces together.
After replacing the delegate methods calls with the new closure
argument, and deleting every related to ApiClientDelegate
inside the class, it should look like this.
Awesome!
By now you should be getting a bunch of errors on the View Model, but don’t worry, we’ll take care of that right away.
View Model
Following the same pattern that we just did in the ApiClient
, we need to:
- Add an
enum
inside the ViewModel class calledResult
- Declare a typealias of type
Result -> ()
calledClosure
- Remove the delegate variable, the init and the extension at the bottom
- Modify the function declaration to have a
Closure
argument - Inside the
get
function, we need toswitch
on theresponse
to get either theresult
or theerror
from the call. - After all of these steps are done, the class should look like this:
And now… Dejavu
The View Controller is now screaming for help, let’s jump right in.
View Controller
The first thing we could do is to modify the View Model init method because it doesn’t expect a delegate anymore, modify the getJobs()
function signature and remove the extension that conforms to ViewModelDelegate
.
As you can see, we can change the viewModel variable to be a let
instead of a lazy var
and now, let’s switch
over the result
just like we did on the View Model.
And that’s that… wait…
We are creating a reference cycle by calling self inside the closure. To fix it let’s use the following capture list [unowned self]
before the (result)
in the closure. There’s a lot of controversy whether you should use [weak self]
or [unowned self]
but for this example, using unowned is the right call.
Run the app, and everything should be working as expected.
Scalable:
In case you have more than two delegates (like in this example) you can quickly scale your enum to have more cases. This is what makes this approach attractive, is clean, easy to follow and easy to scale.
To download the final project, visit here.
Follow me
Twitter: dmlebron