| 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 As 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.