Not so long ago I had published an article about Network Layers and how Swift may help us avoiding big fat singletons by isolating responsibility and simplifying the codebase (it’s on Medium).
Just wanted to say how much I appreciate the time many people took to read it sending lots of comments via mail and twitter.
During the past five months I had the chance to test it on different production projects, discuss it with co-workers and colleagues: the following article aims to propose a more robust and stable iteration of the initial idea: some stuff are changed while others still here, stronger than ever.
In order to keep it readable from anyone I’ll describe it from the scratch and I’ll provide a real implementation you can download and use in your next application.
The common networking layer
Recently there are lot of shiny iOS architectures that are getting more and more hype. As they are all valid and have good and bad parts, they all address the same thing: separate the business logic from the presentation. In this article I’ll describe how to avoid a classic pitfall during iOS development: handle with networking.
But, to explain my point of view, I will first talk a bit about how network layers are often implemented.
In most of them there something called NetworkManager: it’s just a single class (more often a singleton) that contains every single API call in the app.
While it works fine (and apparently make sense) it fails miserably for several reasons:
- Single responsibility: Our NetworkManager contains too many responsibilities to be considered a good practice (if you are interested Marco wrote a good article about it)
- Singleton: often demonized, singletons are not necessarily bad, but they can’t be injected as dependencies.
- Fail for testing: your code can’t be easily mocked when testing; reading raw data or checking why the code crush maybe a pain.
A different approach: what you will get
Before showing how the architecture was designed I’ll show to you what you will get using this approach.
Your next call maybe as below:
- Automatic asynchronous support
- Promise based implementation
- Out-of-box implementation from raw response -> your model
- Strong error handler
While the theory behind this approach is independent from tools used, in order to give a complete out-of-box approach I’ve used the following libraries:
- Networking: in this example the Service implementation uses Alamofire. Switching to NSURLSession is pretty damn easy and, in fact, suggested.
- Async/Promise: I love promises (at least until we’ll get something better with Swift 5) because they are clean, simple and offer a strong error handling mechanism.
Our networking layer uses Hydra (a library of mine), that recently hits the 1.0 milestone.
- JSON: The higher level of the architecture offer out-of-box JSON support with JSONOperation class: everything about JSON was offered by SwiftyJSON, probability the best library for these stuff.
If you like magic you can still plug something like ObjectMapper (frankly I love to control my code and a bunch of code lines does not change my life, especially when you need to debug something).
The following graph shows the architecture of our networking layer:
While it may seems complex, in fact its pretty simple to understand:
- ServiceConfig is responsible to keep the network configuration data (full host address, headers…). You can create it programmatically or (strongly suggested) configuration can be read directly from your app’s Info.plist file. We’ll cover it later below with a cool trick you may not know.
- Service: it’s a concrete implementation of the ServiceProtocol . It’s the core of the networking layer, created via ServiceConfig. This class is responsible to fulfil your requests; in fact its the isolation layer between a request and how it will be dispatched: concrete implementation currently relay to Alamofire but you are free to plug-and-play your own engine (new versions of NSURLSession are pretty good).
- Request & Response: both are concrete representations of RequestProtocol and ResponseProtocol ; as you may imagine they identify a single network request (with all required parameters and facilities) and a response from server.
- JSONOperation & DataOperation: they are the higher level of the abstraction. You will subclass them to provide an out-of-box implementation of the “Raw Request to Model” paradigm.
The article continue on my blog, just click here to continue reading…