Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriseidhof/a16455d2a0b27fa4817a039afc07a24a to your computer and use it in GitHub Desktop.
Save chriseidhof/a16455d2a0b27fa4817a039afc07a24a to your computer and use it in GitHub Desktop.
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment