Working and leading two projects at a time means a great opportunity to experiment with app architecture, and do experiments with other concepts I had in mind or just learned and wanted to try them out. One of topics I learned recently and I think you might find it useful if how I build a network layer now.
Nowadays mobile apps are client-server oriented, so pretty much there is a network layer somewhere in the app, smaller or bigger. I saw many implementations to date but every had some drawbacks. Not thinking the latest one I build has no drawbacks, but it seems to work very well in two projects I am working on currently. And has test coverage close to 100%.
In this article we’ll cover network layer that talks only with one backend, sending JSON encoded requests, so not that complex. The layer will talk with AWS later on and send some files there, but it should be easy to extend its functionality to do so.
Here are some questions I like to ask myself before building such a layer.
- Where to put the code that have knowledge about the backend url?
- Where to put the code that knows about the endpoints?
- Where to put the code that knows how to build a request?
- Where to keep all the code that cares about preparing parameters for a request?
- Where should I store authentication token?
- How to execute requests?
- When and where to execute requests?
- Do I care about cancelling requests?
- Do I need to care about wrong backend responses? Some backend bugs?
- Do I need to use 3rd party frameworks? What frameworks should I use?
- Is there any core data stuff passing around?
- How to test the solution?
Storing backend url
First of all, where should I put the backend url? How other piece of the system
will know where to send requests? I prefer to create a
that stores such information.
Easy to test, easy to configure. You can set the
shared static variable and
access it wherever you want in your network layer, no need to pass it everywhere.
That’s the topic I experimented with for a while before I found a ready-to-go
solution. Tried hardcoding endpoints while configuring
Resource-like objects that knows about the endpoint and can be easily
instantiated and injected, but it was still not what I was looking for.
I came up with idea to create
*Request object that knows which endpoint to hit,
what method to use, should it be GET, POST, PUT or different, how to configure
the body of a request and what headers to pass.
This is what I came up with
A class that implements the protocol is able to provide a basic informations
that are required to build a request. The
NetworkService.Method is just an enum
An example request that maps an endpoint might look like this
To not create the dictionary for headers everywhere we can define extension for
*Request class takes all the parameters needed to make a successful request.
You’re always sure that at least all the parameters you need will be passed, otherwise you can’t
create a request object.
Defining endpoint is easy. If there should be some id of an object that should be included in the endpoint it is super easy to add it because you actually would have such id stored as a property.
The method of the request never changes, the parameters body is easily constructed and very easy to maintain, headers too. Everything is very easy to test.
Executing the request
Do I need any 3rd party frameworks to communicate with the backend?
I see that people are using AFNetworking (Objective-C) and Alamofire for Swift.
I used it many times, but for some time I am not using it. Since we’ve got
that do its job very well I don’t think you need any 3rd party framework.
IMO this dependency is going to make your app architecture more complex.
The current solution consists of two classes -
NetworkService- allows you to execute HTTP request, it incorporates
NSURLSessioninternally. Every network service can execute just one request at a time, can cancel the request (big advantage), and has callbacks for success and failure responses.
BackendService- (Not the coolest name ever but fits quite well) is the class that takes requests (
*Requestobjects described above) related to the backend. It uses
NetworkServiceinternally. In the current version I am using, it tries to serialize the response data to json using
As you can see the
BackendService can set authentication token in headers.
BackendAuth objects is a simple storage that stores the token in
If that would be necessary it could be storing the token in Keychain.
BackendAPIRequest as a parameter of
method and extracts necessary informations from the
request object. This is nicely encapsulated
and backend service just consumes what it gets.
BackendAuth are easy to test
Several questions to cover here. What way would we like to perform network requests? What if we want to perform many requests at a time? How would we like to be notified about the success or failure for requests in general?
Decided to go with
NSOperations that execute network
So, I subclassed
NSOperation and overrided its
asynchronous property to return
Next, because I want to use the
BackendService for executing network calls
NetworkOperation and created
The class creates
BackendService internally so I don’t need to create it
in its every subclass.
Here is how the Sign In operation might look like:
start method the service executes request that is created internally in
the operation’s constructor.
are passed as a callbacks for
request(_:success:failure:) method of a service.
IMO this makes the code more clean, and it is still readable.
Operations are passed to a
NetworkQueue object that is a singleton and can
queue every operation. For now I keep it as simple as possible:
What are advantages of executing operations in one place?
- Easily cancellation of all network operations.
- Cancellation all operations that are downloading images or other operations that are requesting data that is not needed to provide user a basic experience of using the app while network connection is weak. E.g. you would like to prevent downloading images when user is on weak connection.
- You can build a priority queue and execute some requests first to get answer faster.
Working with Core Data
This is the aspect for which I had to delay the publication of this entry. In previous version of the network layer operations returned Core Data objects. The response was received, parsed and converted to Core Data object. This solution was far from ideal.
- The operation have to know what Core Data is. Because I had model detached to separate framework and network layer was in separate framework too, the network framework have to know about model framework.
- Each operation have to take additional
NSManagedObjectContextparameter to know which context it should operate on.
- Each time the response was received and was about to call success block it first tried to find object in a context, or hit the disk to fetch object from disk. IMO this is a big disadvantage. You not always want to create Core Data object.
So I came with idea to take out Core Data out of network layer completely. I created middle layer that are objects created in result of parsing responses.
- This way the parsing and creating objects is quick and not require to hit a disk.
- You also don’t need to pass
- You can update your core data object in the
successblock using the parsed item and reference to Core Data object that you probably keep somewhere where you create the operation - this is my case in most situations when the operation is added to a queue.
The idea of response mappers was to separate the logic of parsing and mapping JSON to useful items.
We can distinguish two type of parsers. The first type return just a single object of specific type. The second type is a parser that parses array of such items.
First, let’s define a common protocol for all the items:
Now, here are some objects that are products of mappers:
Let’s define an error type that will be thrown when something went wrong with parsing.
Invalid- thrown when passed json is nil and should not be nil, or when it is array of objects instead of expected json with a single object.
MissingAttribute- self explanatory, when key is missing in json or when after parsing the value is nil and should not be.
ResponseMapper may look like this
It takes an
obj which is response from the backend - a JSON in our case,
parse method which consumes this
obj and returns
A object that
Now that we have this generic mapper we can create concrete mappers. Let’s take a look on a mapper used for parsing response of Sign In operation.
ResponseMapperProtocol is a protocol to be implemented by concrete mappers
so they share the same method for parsing response.
Then, such a mapper is used in a success block of an operation and you can operate on concrete object of specific type instead of dictionary. Easy to use such object later, evertything is easy to test.
The last thing is a response mapper for parsing arrays.
It takes a mapping function and return array of items if everything is correctly parsed.
Depends on what you expect you might throw an error if just a single item can’t be parsed
or return empty array in worst case as a product of this mapper.
The mapper expects that the
obj (response from the backend) is an array of
Here is diagram that presents the network layer architecture.
You can find an example project here on my github. The project is using fake url for the backend, so no request will finish with success. I made this available only to give you a view of how the foundation of the network layer look like.
I found this way of doing network layer very useful, simple and easy to work with.
- The biggest advantage of it is that you can easily add new operations that will have similar design to others and that it does not know anything about Core Data.
- You can keep the code coverage close to 100% with no big effort, without thinking how to cover a super hard cases, because there is no such cases at all.
- The core of it is easy to reuse in other applications that have similar complexity.