Architecting an iOS networking layer: Part 2

A sample project including unit tests to demo our clean architecture

Melissa Yung
Bleeding Edge
3 min readApr 1, 2019

--

Illustration by Marta Pucci

Welcome to Part 2 of this two part series, where we go through the networking layer we use in our iOS app at Clue.

As a follow-up to Part 1, where we went through the main building blocks of our networking architecture, we would like to now show it in action in our demo app.

This demo app consists of a simple view with a button that is used to fetch forum posts. Based on the success of the request, the UI is updated to reflect the number of fetched posts. We will use a fake online REST API https://jsonplaceholder.typicode.com/ to achieve this.

The View

For simplicity's sake, we will only focus on the bit that performs the network request i.e. fetchPosts.

A. The view needs to keep a reference to the fetchPostsRequest instance to ensure that it doesn't get deallocated prematurely.

B. The view puts together the required building blocks to make the service call i.e RequestConfiguration, HTTPClient, Request and RequestEventListenerFactory

The Request Object

A.1 and A.3 The request conforms to the HTTPClientDelegate protocol as we want to make a service call and handle its success or failure.

A.1 and A.2 The request conforms to the RequestObserving protocol as we have interested listeners that want to be notified of the results once the service call is complete. This means that it has an eventListenerFactory which will provide a list of listeners.

B. Both the required HTTPClient and the RequestEventListenerFactory are injected to the constructor to ensure testability.

C. We need to set the request to be the delegate of the HTTPClient so that we can be notified when the service call returns.

D. The only public interface is the actual call to perform the service call. It is public since this class lives in the networking layer module which is separate from our main app.

E. The Request knows how to construct the request data required to make its service call i.e the method type, the path and any required parameters.

The Request Unit Test

Now, let’s look at the how we tested our FetchPostsRequest class.

In order to instantiate our FetchPostsRequest, we first need to mock the two required parameters

  • MockHTTPClientProvider which conforms to HTTPClientProviding
  • MockRequestEventListenerFactory which conforms to RequestEventListenerFactory. This factory also requires a MockRequestListener.

We can then fully test our Request class and ensure that

A. The delegate is properly set

B. The request data is properly set

C. The Request parameter is properly sent to the server

D. The success response is properly notifying the listeners with the response

E. The success response is properly not notifying our listeners if it’s nil

F. The success response is properly not notifying our listeners if it’s a malformed response

G. The failure response is properly notifying the listeners of the error

Conclusion

We are pretty happy with this architecture so far. It allows us to

  • easily add new requests including incrementally migrating old requests to this new architecture. Moreover, we can fully unit test our requests as well as their response handling.
  • modularise our app by moving all the networking layer in its own module and separating it from the app's business logic. This in turns allows us to easily replace our underlying third-party networking library if need be.

You can find the demo app here.

Thanks for reading! ❤️

If you’re curious about Clue, check out this description of the app, and our Jobs page.

--

--