title | synopsis | tags | share_image | episode | book | collection | author |
---|---|---|---|---|---|---|---|
Swift Tip: Protocols vs. Values |
Extensible in different ways |
news |
/images/blog/2019-05-13-protocols-vs-functions.png |
19 |
advanced-swift |
networking |
chriseidhof |
Last week, we looked at the differences in extensibility between enums and protocols. It's also interesting to compare protocols and regular values. For example, take the Resource
struct from our Networking collection. It looks like this:
import Foundation
struct Resource<A> {
var request: URLRequest
var parse: (Data) throws -> A
}
We then could add an extension for all A
s that are Decodable
:
extension Resource where A: Decodable {
init(get url: URL) {
self.init(request: URLRequest(url: url)) { data in
try JSONDecoder().decode(A.self, from: data)
}
}
}
And finally, we can create an instance of a Resource
:
struct Country: Codable {
var alpha2Code: String
var name: String
var population: Int
}
let sample = Resource<[Country]>(get: URL(string: "https://restcountries.eu/rest/v2/all")!)
(For the full code, see this gist)
A popular alternative approach is to create a protocol instead of a struct Resource
, something like this:
protocol Loadable: Decodable {
var request: URLRequest
}
extension Country: Loadable {
// ...
}
Unfortunately, here we run into a limitation of protocols: each type can conform to a protocol at most once. For example, what if we wanted to load a Country
from multiple endpoints? Or what would the conformance for an Array
look like?
This is also a problem for protocols such as Codable
. If Apple ever provides conformance for CLLocationCoordinate2D
, it has to pick a single representation. In the API above, a location coordinate is represented as an array of numbers, but we've also used APIs where it's represented as {lat: 39, lon: 22}
or {latitude: 39, lon: 22}
. In a previous post, we show a solution to this problem, but it's not ideal.
For some APIs, a protocol is a great choice (Collection
, Equatable
, Hashable
, etc.). However, when designing your own APIs, think about whether it makes sense to "conform" a type multiple times. If yes, try using values or functions rather than protocols. We think the extra flexibility can be really nice.