Swift — Handle Rest routes like a Boss
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