Protocol-Oriented-Networking in Swift

Join me for a Swift Community Celebration 🎉 in New York City on September 1st and 2nd. Use code NATASHATHEROBOT to get $100 off!

I recently gave a talk on Practical Protocol-Oriented-Programming(POP💥) in Swift. The video is still being made. Meanwhile, here is the written-up version of the POP Networking part of the talk for reference (for me and anyone else!).

The Usual Setup

Let’s say we have an app that displays pictures and information of food from around the world. Of course, it would have to fetch the information from the API. To do that, it is common to have an object that does the API request:

Since we’re making an asynchronous API call, we cannot use Swift’s built in Error Handling that either returns the correct response or throws an error. Instead, it is good practice to use the Result enum. You can read more about the Result enum here, but it is basically this:

When the API call is successful, a Success result with the correct payload of objects is passed into the completion handler – in the case of the FoodService, the successful result includes an array of Food objects. And if it’s unsuccessful, the Failure result is returned, with an error depending on the error that happened (e.g. 400).

The FoodService’s get function (to make the API call), is usually called in the ViewController, which decides how to handle the Result based on a Success or Failure scenario:

But there’s a problem…

The Problem

The Problem with the ViewController’s getFood() function is that it is the most important function in that ViewController. After all, it cannot display food on the screen if the correct API call is not made and the result (whether Success or Failure) is not handled correctly.

To make sure it does what it’s supposed to do (and that an intern or future you doesn’t accidentally change it to get deserts instead of all food), it is important to write tests for it. Yes, View Controller Tests 😱!

Seriously, it is not that bad… There is just this one weird trick to get your View Controller tests set up!

Ok, so we’re ready to test our View Controller. What’s next?!!!

Dependency Injection

To test the ViewController’s getFood() function properly, we need to inject the FoodService (the dependency) into it vs just calling it straight in the function!

This gives us at least a start to our tests:

Now, we need to have a lot more control over what result the FoodService actually returns…

Protocols FTW

So this is our current version of the FoodService:

For testing purposes, we need to be able to override the get function, to control which Result (success or failure) is passed to the ViewController, at which point we can then test how the ViewController handles a success vs failure.

Since FoodService is a struct, we cannot subclass it. Instead, you guessed it!, we can use protocols.

We can actually take out most of it’s functionality into a simple protocol:

Notice the associated type. This protocol will work for all our services. In this case we’re using it for FoodService, but of course you can use the same protocol for the CakeService or DonutService. By using this generic protocol, you’re adding very nice uniformity to all the services in your application.

Now, the only thing that changed in the FoodService, is that it conforms to the Gettable protocol. That’s it!

The other big benefit of using this approach is readability. By looking at the FoodService, you immediately see that it is Gettable. You can use this same pattern to also implement Creatable, Updatable, Delectable, etc, and you’ll immediately be able to see the service’s capabilities as soon as you look at it!

Using the Protocol 💪

So now, time to refactor! In the ViewController, instead of passing in the FoodService to the getFood method explicitly, we can constrain it to receive a Gettable that has [Food] as it’s associated type!

Now, we can easily test this!

Test All the Things!

To test the ViewController’s getFood function, we need to inject it with a Gettable that has [Food] as it’s associated type:

So now, we can just inject the Fake_FoodService to test that the ViewController does call a service that returns [Food] as a successful result and that it correctly assigns the [Food] as it’s data source for populating the TableView:

You can now of course also write a test for the different Failure scenarios as well (e.g. what error messages are displayed based on the ErrorType).

Conclusion

Using Protocols for the Networking layer makes your code UNIFORM, INJECTABLE, TESTABLE, and READABLE!

POP all the things!

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.