A quick hands on Protocol Oriented Programming in Swift
First of all, I won’t get into why Protocol Oriented Programming can be better than inheritance or such. There are a lot of materials out there covering this topic so you can make your own opinion.
I just want to share with you some cool stuff I learn along the way. I’m currently in the process of rewriting my Objective-C library WANetworkRouting to Swift. Btw, we are using this library in many apps at Wasappli, and you should give it a try: with a few configuration lines, you can get your app connected to a REST webservice and the data cached to CoreData in minutes.
I could have been saying: I’ll just translate to Swift my code, but I wanted to dive in the new ways of thinking provided by the language. Being an Objective-C developer for the last 8 years makes it really exciting to shake my habits.
One of the things I love in Swift is how you can use protocols to add behaviors to your class. Instead of using inheritance, you can get rid of any hierarchy and flatten the stack of behaviors. You also have the ability to precisely decide which behavior to adopt instead of having a bunch of spaghettis at some point. Doing so also encourage you to write a more testable code 👍
Let’s dive in the code: one of the feature of WANetworkRouting is the ability to do something like GET myObject
without the need to know which is the route on your API.
In the O-C library, I had a component called the WANetworkRouter (obviously) registering WANetworkRoute which has some properties like pathPattern
(ex: shelves/:itemID
), httpMethod
(POST
, GET
, …), and the object class. This was mainly inspired by RestKit and a convenient way of configuring once and for all the library to keep things separated.
For example, you could be writing this
And then, by calling this method, the router would build the path against your API.
Let’s move on Swift
I could have wrote something similar, but I wanted to follow the trend. Yeah, let’s be honest, a lot of programming concepts around Swift are not new, just a new cool way of doing things. Sometimes, I feel like it’s complicating the codebase but I’ll save it for an other article 👐
First, I created a Routable
protocol with what I need:
Notice that I do not wait on returning an URL or a dictionary, but an object conforming to respectively URLConvertible
and APIParametersConvertible
which are also protocols. They do nothing except func asURL() -> URL?
and func asDictionary() -> [AnyHashable: Any]
.
On a User class, that would give something similar to:
So we quickly see that we’ll write the same thing again and again accross the objects.
That’s when the fun starts. Let’s create a new protocol: RESTRoutable
which inherits of Routable
(yeah protocols can inherit from others 🤘) :
We now need to make it sweet for a REST api by defining the need to get a base URL, a base path and the identifier:
All right, that’s great but where is the fun in that? 🙈
Well, with Swift, you can extend your protocols to gave them default behaviors. And this is what we’ll do:
First, I’m not dealing with the parameters
from Routable
, this is not the purpose here. You can see that we are building the url
the same way I did in my implementation on User
. Don’t wonder too much about resourceIndexURL
method, it’ll be explained in the advanced topic.
Also, static var basePath
is just a fancy thing to turn the class name into it’s plural (without counting the case where we get es
or other behaviors like Story
to stories
). But for some cases like User
, you’d automatically get users
. Isn’t that sweet 🍬?
Let’s finish with our User
and conform it to RESTRoutable
instead of Routable
:
And that’s it, you now have a user ready to return www.myapi.io/some/path/users/1234
when asking for a GET user(1234)
! 🙌
As a conclusion
I’d say that this is simply an other way to do something. I could have done something similar with Objective-C using a protocol to return the route from an object. But I’d have been stuck with theRESTRoutable
thing since there is no such thing as extending protocols in O-C. It could have been achieved by an extension on the object type you’re using (like NSManagedObject
) but this is really tightened to your implementation and we are trying to avoid this.
What I really like here is how you can get something readable without caring about the object type you’re using, and by keeping the compiler in the loop to help you fill the blanks from the protocol you’re using.
The edge case of this is: by providing default implementation in a protocol extension, you can hide behaviors from your users which do not read the doc or the original codebase. So they never find out how to use it or tune it.
Here, I think I’m in the middle of this since RESTRoutable
is a other protocol which inherits from Routable
. I’m not providing a default REST behavior directly on Routable
.
For advanced users, I’ll soon write something on how I use it on my Swift revamping of WANetworkRouting and attach the link here.
Thanks for reading it, and if you enjoyed please leave some claps and share it! Muchly appreciated!