Swift protocol extensions powering network abstraction and deserialization

A while ago I wrote about how I started using protocol extensions to help with network calls abstraction.

As I continued with this approach, new requirements arose, and eventually every time I needed to make a new request I was already cooking up a bit of boilerplate. Take a look at the following example:

How a tipical caller object would look like

I need to tell the requester class the url, the HTTP method, the headers, the body, the encoding, and how to deserialize the data in case of a successful response (soon probably the same for failure but not the point now).

(if you want to see the requestable protocol, check it here: https://gist.github.com/nunogoncalves/027979ddb89d7ebccee7ff42edfa22d3 )

I have started tearing this class apart, moving some specific behaviours out into protocols with their own implementations. For example:

Sometimes I need fill headers to add extra headers after init is called, hence the fillHeaders method

This way, I can remove all headers’ boilerplate out of most requester classes. And of course I also benefit from having this constrained to one place, in case I need to change the bearer token for example.

Doing this for the rest of the different behaviours, one gets a class that is much easier to read (Is it really? — more thoughts on that later), and applies the single responsibility really well. Each of the behaviours is now well contained within its own file.

So the Users.Getter usecase now conforms to multiple protocols which tells us what each one does without the need to worry about how, in a much simpler way than having all that previous boilerplate code bacause all of those protocols implement the default behaviour.

Note: the getUserDetails(success:, failure:) could very well be removed. But in my opinion Users.Getter(_:).getUserDetails(success:failure:) reads better than Users.Getter(_:).call(success:failure:) which is the method of the first protocol on the next list:

  • Requestable — States that this class makes a network request somehow. It gathers and builds all necessary information to pass to the class responsible by the actual call.
  • Authenticable — Sets the authorization header with the user access token.
  • WebGetter — Sets the HTTP method to “GET”
  • URLEncodable — Sets the encoding to URLEncoding.
  • EmptyBody — Sets the body paramenters to be empty
  • Deserializable — This is actually the main reason for this post. It shows this class has a deserializer which builds a bussiness object from a dictionary coming from a success response.

How does Deserializable work?

It is a generic protocol with a constrained type needing to be a Deserializer class which is also a protocol. The deserializer defines the behaviour of converting a dictionary into a MappedObject. It’s a generic protocol because the conforming class can generate any type of object it wants.

The var deserializer: T { get } tells the conforming class it has to define a deserializer class that is of type Deserializer which has to implement buildFrom(_:).

With an extension, we can now give the Deserializable protocol a default behaviour that is calling the buildFrom(_:) method from the deserializer, which leaves the conforming class with just the need to identity the deserialier class. The developer has two things he needs to do:

1 — Build the deserialiser class and have it conform with Deserializer protocol implementing buildFrom(_:) and returning the desired object.

2 — Identify the Deserializer class as the one built on the previous bullet.

Finally for a common request, all the developer needs to do is have the class conform to the available protocols (pretty good SRP) and declare a deserializer class.

I’ll just post the before and after gists for better comparison:

The Users.Getter class now gets much easier and nicer to read.

But does it really?

I really like this solution, it seems elegant and readable. But at the same time I have my doubts. I mean, most of the behaviours are now hidden away on multiple files the developer has to know and search for. I want and like to think that if I don’t touch this code for a year, I come back and can still fully understand it right away. Or if a new colleague joins the team, he finds the pieces instead of starting all over.

I’m still not 100% sure if this is better than to have to copy and paste a bit of boilerplate or hide away everything to make some code more elegant. This way makes me open a whole bunch files to understand the full picture. On the other hand, I probably don’t want nor do I need to worry about most of the implementation details most of the time, which are hidden within those protocols. Want a get request? Conform to WebGetter, want a JSON enconding request? Conform to JSONEncodable. Want a request that is authenticaded? Conform to Authenticable. Don’t worry about the rest.

I’m still not sure where the “this is too much” line is. Is less code always better? Definetely not, but for now I like it and I’m gonna stick to this approach and see how it works.

If you have any comments or suggestions leave them bellow, or let me know on twitter @goncalvescmnuno