Cocoaphony

Protocols III: Existential Spelling

This was supposed to be a quick sidebar, but it turned into a full-length article, so I’m calling it part 3. The original part 3, continuing the network stack, is mostly done, but I wanted to explain this weird word “existentials” first, and it turned out longer than I’d expected. Blame Joe Groff; he’s written too much interesting stuff lately and I want to talk about it.

If you’re interested in the future of generics in Swift, Joe Groff has a must-read post called Improving the UI of generics. (You should also read the linked Generics Manifesto for background.) In it, he touches on a common confusion in Swift. If you don’t understand what he’s talking about here, don’t worry. Explaining this paragraph is the point of this article.

We gave existential types an extremely lightweight spelling, just the bare protocol name, partially following the example of other languages like Java and C# where interfaces also serve as value-abstracted types, and partially out of a hope that they would “just work” the way people expect; if you want a type that can hold any type conforming to a protocol, just use the protocol as a type, and you don’t have to know what “existential” means or anything like that. In practice, for a number of reasons, this hasn’t worked out as smoothly as we had originally hoped. Although the syntax strongly suggests that the protocol as a constraint and the protocol as a type are one thing, in practice, they’re related but different things, and this manifests most confusingly in the “Protocol (the type) does not to conform to Protocol (the constraint)” error.

Protocols II: A Mockery of Protocols

In the last section, I ended my little network stack at this point:

// Something that can be fetched from the API
protocol Fetchable: Decodable {
    static var apiBase: String { get }  // The part of the URL for this type
}

// A client that fetches things from the API
final class APIClient {
    let baseURL = URL(string: "https://www.example.com")!
    let session: URLSession = URLSession.shared

    // Fetch any Fetchable type given an ID, and return it asynchronously
    func fetch<Model: Fetchable>(_ model: Model.Type, id: Int,
                                 completion: @escaping (Result<Model, Error>) -> Void)
    {
        // Construct the URLRequest
        let url = baseURL
            .appendingPathComponent(Model.apiBase)
            .appendingPathComponent("\(id)")
        let urlRequest = URLRequest(url: url)

        // Send it to URLSession
        let task = session.dataTask(with: urlRequest) { (data, _, error) in
            if let error = error {
                completion(.failure(error))
            } else if let data = data {
                let result = Result { try JSONDecoder().decode(Model.self, from: data) } 
                completion(result)
            }
        }
        task.resume()
    }
}

This can decode any Fetchable model from an API endpoint that has a URL something like https://<base>/<model>/<id>. That’s pretty good, but we can do a lot better. A natural first question is “how do I test it?” It relies explicitly on URLSession, which is very hard to test against. A natural approach would be to create a protocol to mock URLSession.

I hope by the time you’re done with this series, hearing “create a protocol to mock” makes you flinch just a little.

Protocols Sidebar I: Protocols Are Nonconformists

Last time, I mentioned something in passing:

I need a new protocol.

protocol Fetchable: Decodable {
    static var apiBase: String { get }
}

I need a protocol that requires that the type be Decodable, and also requires that it provide this extra string, apiBase.

Read that again. It requires that the type be Decodable and also requires other things. I didn’t say that Fetchable is Decodable. It isn’t.

Protocols I: "Start With a Protocol," He Said

In the beginning, Crusty

In 2015, at WWDC, Dave Abrahams gave what I believe is still the greatest Swift talk ever given, and certainly the most influential. ”Protocol-Oriented Programming in Swift,” or as it is more affectionately known, “The Crusty Talk.”

This is the talk that introduced the phrase “protocol oriented programming.” The first time I watched it, I took away just one key phrase:

Start with a protocol.

And so, dutifully, I started with a protocol. I made a UserProtocol and a DocumentProtocol and a ShapeProtocol and on and on, and then started implementing all those protocols with generic subclasses and eventually I found myself in a corner.

Protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements

And then I started throwing things.

Refactoring Slow and Steady

I’ve been talking with folks on a Slack about refactoring today, and I thought I’d put some of my thoughts here. Maybe a little less polished than I’d like, but I wanted to get them out of my head and down on “paper.”

Talking and Teaching

Pedro Piñera makes some important points in his article In a world…. There are a number of things in there, and you should go read it, but I want to focus on one part, which is the observation that the core “iOS speaker circle” is a fairly small group of people. Pedro notes:

There’s a huge difference when you compare a talk from someone that has been working a lot on the topic and from someone that studied the topic for giving a talk. Why do people do it then? Talks with a lot of value usually come from unknown people. From these people that from the anonymity worked on a topic and they achieved something that they were willing to share. … People don’t care about the company that person had worked for, or the newsletter that the person had written, but instead, what that person wants to share.

While I agree with Pedro’s concern, I disagree that this is the proper ideal.

Copying

I’m on my way back from try! Swift, which was fantastic. Of course it had those obvious things I’d hope for. Interesting talks, friendly people. Making new friends, and reuniting with old ones. But it also had some surprising delights and lessons.

I travel pretty well, but sometimes I make mistakes, and this was one of those times. The deodorant I thought I’d packed turned out to be body wash. Now there are a dozen reason that this shouldn’t really matter, and wouldn’t really matter given the A/C and the weather, etc., but I’m a product of my culture, and it was a bit stressful. I tried to find a drug store on the way to the conference, but I was afraid of being late and finally resigned myself to accepting things as they are and moving on.

And then, in the conference rest room, I discovered a small cache of toiletries under a try! Swift sign saying “if you need one, please take one.” I was dumbfounded. It was a very small kindness, but it mattered to me.

Inspiration

As a speaker, writer, and member of our community, Daniel Steinberg is my inspiration. That’s not a secret. If you and I have spent much time talking after a conference, I’ve probably mentioned it. It’s not the sort of thing you usually say to someone, and I don’t think I ever have, but I’ve learned a lot from his speaking style, and I constantly try to live up to his standard of kindness. I’m not by nature very kind, so if you see me behaving that way, I’m likely trying to do what I think Daniel might.

This isn’t to say we’re close. We see each other at conferences. We email and tweet. We’re members of a community. So of course I have some idealized picture of him in my mind, without all the this and that of a real person. Even so, he inspires me. When my wife, Janet, and I talk about what’s ahead for our future, we always talk about Daniel and his wife, Kim. I’ve seen them at conferences, traveling together, independent but a team, and I think, hey, we could pull that off when the kids go to school. And Janet and I talk about how to make that work. And we know we only have the shallowest understanding of their real lives, but they inspire us.

Kim died this week. I didn’t know her well. We’d met, and she was nice to me. She and Daniel always seemed so “together” even when they were apart most of the day. Hearing the news unmoored me and scared me and made me think about myself and my family and my plans. And then my heart broke for Daniel.

I’m not kind by nature. But I’m part of a community that is, and part of that is Daniel’s influence on us. Ellen Shapiro has been kind and set up a fund to let us support SmileTrain, where Daniel and his daughter Maggie have asked us to give in Kim’s name. I had never heard of SmileTrain, but it is so perfect. It is kind. And it is practical. It is literally the gift of a smile. It is Daniel and Kim.