Codementor Events

Swift — Handle Rest routes like a Boss

Published Oct 31, 2017

Unless you’re using the now very popular Parse platform, chances are your App deals with a RESTFUL Api. In this article we will see a clean way to generate rest routes from our swift models.

What we want

For a given customer model, we want to generate the associated REST route.

let customer = Customer(identifier: 1234)
// We want to generate "/customers/1234"

Of course we do not want to add this information in our models because the models should NOT depend on the route building logic. The url generation should sit on top of models.

Let’s code

With Swift, as usual, you start with a protocol.

So let’s ask ourselves, conceptually speaking, what is a rest resource? Well a rest resource has a rest name, for example our Customer class (or struct👍) will have to generate a “/customers” url. And each resource has a unique identifier.

Here is what it looks like translated into swift code :

protocol RestResource {
    static func restName() -> String
    func restId() -> String
}

Now let’s conform to the protocol (or implement the interface for the java guys still alive out there #easytroll )

extension Customer:RestResource {
    
    static func restName() -> String {
        return "customers"
    }
    
    func restId() -> String {
        return "\(identifier)"
    }
}

Finally let’s build our URL logic. Basically we just want to grab the rest name and append the rest identifier, nothing groundbreaking here :

func restURL<T:RestResource>(r:T) -> String {
    return "/\(T.restName())/\(r.restId())"
}

Notice here that we could have written the same function without generics by matching the underlying Resource Type at runtime via r.dynamicType. But let’s keep dynamic stuff for when it’s really needed, the compiler has got our backs on this one 😃

And voila!


let customer = Customer(identifier: 1234)
restURL(customer) // -> "/customers/1234"

Bonus

Time for a treat.

Let’s suppose all your structs or classes share a unique identifier which is often the case like so :


struct Customer {
    var identifier:Int = 0
}

struct Product {
    var identifier:Int = 0
}

struct Order {
    var identifier:Int = 0
}

It’s pretty boring to have to repeat boilerplate code for restId() since it’s always going to be the same code aka return “(identifier)”

That’s where swift 2.0 comes in 😃 Let’s DRY that out with protocol extensions shall we?

First create Identifiable protocol and make your models conform to it :

protocol Identifiable {
    var identifier:Int {get}
}

struct Customer:Identifiable {
    var identifier:Int = 0
}

struct Product:Identifiable {
    var identifier:Int = 0
}

struct Order:Identifiable {
    var identifier:Int = 0
}

Now provide a default implementation for restId() in a protocol extension #likeABoss :

extension RestResource where Self:Identifiable {
    func restId() -> String { return "\(identifier)" }
}

Now your routes file looks super clean and concise, congrats! \o/

extension Customer:RestResource {   static func restName() -> String { return "customers" } }
extension Product:RestResource {    static func restName() -> String { return "products" } }
extension Order:RestResource {      static func restName() -> String { return "orders" } }

Adding a new route is just a matter of adding a new line in this file ❤️ Here is the link to the online Playground, have fun 🎉

Special thanks to YannickDot for proofreading

Originally published here

Discover and read more posts from Sacha Durand Saint Omer
get started