Swift's 2 biggest missing features

by Aaron Hayman in

Before I truly begin this post, I feel the need to run a few qualifies here. I'm going to spend some time ranting about what I consider are some obvious omissions in Swift, but I need to be clear: I actually, really like Swift. I really do. Despite being a "long time" (depending on your perspective) objective-c developer, Swift truly feels like programming in the future. I do things with Swift I never do in objc... good things. Swift helps me abstract like I never could(?) in objc, or just perhaps it encourages me to abstract where objc never did... not sure. But what I do know is I find myself creating better abstractions easier, allowing me to reuse logic in some pretty incredible ways. I really like Swift.

And then, I'm going along and hit a wall at 90 mph. Really!? I can't do that? Why the hell not? What the hell was I creating all this architecture for anyway? Three days later I have a solution but I kind of feel lied to... where was all the power I was promised?

And it's not as though I'm just trying to figure out Swift for the first time. I've created entire apps in Swift. Pretty much all of my work is done in Swift now. I know Swift, which really, is where these complaints are coming from. And so, without further ado... let's add some ado:


Many of my complaints come from a task I was given at work: after much deliberation we had settle on a persistence mechanism, Realm, and I was to write an abstracted Persistence Layer around it so that we didn't consume our database objects in the app itself. We, like so many others, face the future with open hearts and dammit, Swift is the future so we're going do this thing.

Now I'm sure a lot of people will wonder why we just don't use Realm directly. I have a lot of opinions about Realm. Those will have to wait for another, but suffice it to say that Realm is to me that place where genius and idiocy meet. That being said, I came across many blogs and github issues that declared my intent to be impossible. Most had given up to simply consume Realm in the app, but I'll be damned if I let the dead bodies littered along this path keep me from my goal, dragon or none. And let me tell you, here be dragons:

Covariant Custom Generics

To be fair, I fully understand why Apple did what they did with Swift. Swift is one of the strongest typed languages out there and Apple tried to do this without requiring developers to know what covariant, contravariant and invariant types are. You can't claim a "simple, easy to learn language" and require a knowledge of mathematical categories. So they put in reasonable defaults that make sense for most developers. Type declarations are covariant. Yep. Function signature are contravariant. That makes sense, you want to be able to generalize functions. Collections are covariant. Definitely, as well they should be. Custom generics are invariant. Sure.. wait what? Why?

A brief explainer (if you already know what covarient means, feel free to skip this). When we speak of "variance" we're talking about allowable type substitutions. Basically, covariant means that for a given type: Animal, you can pass in for that type a subtype: class Dog : Animal. So say you have an array: var animals: [Animal] = [] you could append a dog: animals.append(Dog()) and the compiler would simply smile. But invariant types do not have this allowance. So let's say I create a custom collection: class Collection <T> and use it to contain Animal: var collection: Collection<Animal> = Collection(). If types were invariant and I attempt to pass in Dog() into that container, the compiler would throw an error: 'Animal' is not identical to 'Dog'. Our custom generic is invariant and so we can only ever use that exact type the generic is defined with. But this is a contrived example. Swift does allow you to pass in Dog because Type declarations are covariant. Yay! But what the hell am I complaining about then? Well, let say you have another class: Coordinator that coordinates your collections. It handles various collections of: Collection<Animal>. What happens if you attemtp to pass in Collection<Dog> in when Coordinator requires Collection<Animal>. Now you run into the Swift compiler full speed: 'Collection<Animal>' is not identical to 'Collection<Dog>'. You cannot pass in Collection<Dog> no matter how hard you try... trust me, I tried.

This to me seems to be a heinous omission on Apple's part. It's a cheap escape. I'm absolutely certain that invariant custom generics are soooo much easier on the compiler. And I have to admit, it's much easier to for a new developer to understand. I suspect that for many people, typed generics take a little getting used to and just the idea of passing a generic into another generic is bound to produce headaches. Hell, I'm all for making invariance the default but dammit, at least give us a choice. Let us allow covariance in our generics. It's hell trying to get around the variance problem when you need to generalize a container. And when you're trying to write a Persistence Layer over a database implementation, that's exactly what you need to do.

In this more than anything I'm hoping it's simply a matter of "we haven't had the time to implement it". I'm truly hoping that Swift 2.1 brings about this change.

True Protocol Generics

This is what I want:

protocol Cache <T> {
  func addToCache(object: T)

Anyone who's tried this will know it's not allowed. Instead we're stuck with a very weird syntax:

protocol Cache {
    typealias T
    func addToCache(object: T)

If that weren't weird enough, try storying a cache:

`var cache: Cache<User>`

and you get: Protocol 'Cache' can only be used as a generic constraint because it has Self or associated type requirements. I spend over an hour convincing a Java developer that this was actually an error. He was aghast that you couldn't actually store a generic protocol like this. To be honest, so am I. I really don't know why you can't do this but I can definitely say it cramps my style.

I love protocols, and I'm very much for protocol based development. Apple claims it also loves protocols and even touted such at WWDC this year, but until they fully flesh out this issue I find that protocols are pretty limited.