Adventures in Swift

Now that I’ve scrapped the idea for my current project of a tabletop interface interface that can be approached by multiple users from either side, I haven’t yet found another compelling reason to use the Perceptive Pixel in particular. As I’ve started to think through the implementation itself, I’ve also realized that
my project, as compared to others, is going to require far more work in low-level implementation. For instance, there are only a couple of projects (including mine) where we’re going to have to dig into the nitty-gritty of gesture recognition, as opposed to having the convenience of having this work already done for us done in a library. I wouldn’t have thought to scrap the Perceptive Pixel idea unless I knew of a better alternative. Fortunately, that better alternative was present for me in a touch platform that I already know. I’ve been developing on iOS sporadically since 2009, using it for this project would at least allow me the advantage of not having to learn an entirely new platform/framework/etc. in order to develop for the Perceptive Pixel. For these reasons, I’m now developing my project for the iPad.

I’d heard from several people that Swift–Apple’s new language for iOS and OS X development–was enjoyable, at least, to use. I jumped right in, read up a bit, and decided to use Swift for this project, as well. This post, then, is a write-up of a couple of the more interesting and/or frustrating things I’ve come across in developing my first NUI using Swift.

First, there are plenty of resources out there touting Swift’s interoperability with Objective-C, C++, and C. As all other tools (Apple and third-party frameworks and libraries) are written in one of these other languages, this is a must. However, when it comes to interoperability, I’ve hit one major roadblock: class methods on ‘bridged’ Objective-C classes. See, if you want to use Objective-C classes in your Swift project, you bring them in with the use of a bridging header. When a given Objective-C class/framework/library is imported into Swift, it’s public API is remapped to something usable in Swift. This largely affects the arguments and return types those remapped functions and methods are expect to get from and to give you. In general:

  • Certain Objective-C types are to their equivalents in Swift, like id to AnyObject. id is Objective-C’s pointer-to-anything, and AnyObject is an optional type in Swift that can wrap an object of any type, much like a Maybe in Haskell.
  • Certain Objective-C core types are remapped to their alternatives in Swift, like NSString to String
  • Certain Objective-C concepts to matching concepts in Swift, like pointers to optionals

Interacting with the API that’s exposed to you using Swift is fairly straightforward. For more information, Apple has made a start at documenting this process. I say ‘made a start’, because there are some places where this documentation still sucks. One of these places is in describing how Objective-C class methods are mapped to Swift. Not understanding this is something that has given me quite a few problems, especially when it comes to interacting with third-party (non-Apple) code that is only available in Objective-C. Here’s the documentation that Apple does give for this:

For consistency and simplicity, Objective-C factory methods get mapped as convenience initializers in Swift. This mapping allows them to be used with the same concise, clear syntax as initializers. For example, whereas in Objective-C you would call this factory method like this:

In Swift, you call it like this:

In other words, all class methods (what Apple is calling ‘factory’ methods) and all initializers on your Objective-C classes get mapped to a convenience initializer in Swift. This essentially means that all Objective-C class methods and initializers on some class MyClass get mapped to some different signature of MyClass() call. My first problem here is that not all class methods are necessarily factories. Beyond that, though, Apple really doesn’t give us any explicit information as to how this mapping takes place.

We do know this much: a call to the sole initializer of an Objective-C class that takes no arguments is mapped straight across:

That’s easy enough. For initializers that do take arguments, I’ve come to resort to relying on code completion in order to figure out how different those initializer signatures are mapped. If I have this Objective-C class, for instance:

The bare initializer is straightforward. There’s some changing of parameter names that goes on under the hood for those initializers that do take parameters, however. These are the respective calls in Swift:

First, it looks like changes to parameter names only occur for the first parameter. Got it. Next, for those first parameters, ‘init’ is always stripped off of the parameter name, so -[MyClass initWithFoo:andBar:] becomes MyClass(foo:, andBar:) in Swift. Second, ‘with’, if used, is also stripped from the first parameter name. Now, having two intializers with signatures -[MyClass initWithFoo:andBar:] and -[MyClass initFoo:andBar:] is certainly poor style, but these two initializers will map to the same method on MyClass, namely -[MyClass]. If you have defined similar methods to these, and each has differing behavior, you’re not going to see the results you expect to see. I’m not entirely certain that I understand Apple’s motivation in stripping words from parameter names; I’ll assume it’s an effort at maintaining succinctness. But, Apple’s stump speech with Swift has been all about safety, and I’m not sure this behavior is in line with this goal.

Similar things happen with class methods (or according to Apple, ‘factory’ methods). They’ve given us one hint in the previous example: +[UIColor colorWithRed:green:blue:alpha:] maps to UIColor(red:, green:, blue:, alpha:) in Swift. So, it’s clear that something is going on here with the first parameter name. And, if you guessed that it has to do with the fact that a suffix of the class name is also a prefix of the first parameter name, you’d be right. It seems that any portion of the first parameter to a class method that is a suffix of the class name itself, is removed from the parameter name. This causes two problems. First, as documented here, all of these three class methods map to the same initializer:

Wow–that’s annoying. Do you see the second issue, though? What if I have a class method with the signature +[FancyDoodad doodad] that does a whole bunch of grunt work in setting up my FancyDoodad? To what initializer does this class method map in Swift? That’s right–the bare initializer. ‘doodad’ is stripped out and it’s left with nothing to which to map other than the bare initializer, FancyDoodad(). Can I work around this in my own Objective-C classes that I write and intend to use with Swift? Of course. But, I’ve yet to come across a case where I’ll need to write some helper class in Objective-C. Most of the Objective-C I use now are old things that I’ve written, or more often, third-party libraries. What to do when one of these libraries gives me a class method that makes their class so much more convenient to use, but follows this (very common) naming pattern? Well, I’m left with two obvious options: either go and change the class method signatures in those third-party classes (which would be an enormous mistake), or write a category on the third-party class that monkey patches in a new method that can get at the original Objective-C method. Will that work? Yes. Is it a pain in the ass? Also, yes.

So, that’s one of the pitfalls I’ve come while working with Swift and Objective-C in developing this NUI. And, considering that this last issue tramples code that follows a very common pattern in the Objective-C community, I’d say that Apple has screwed the pooch on this one. It’s certainly fixable, and I think it behooves Apple to do so. I’ve come across a number of other issues (getting used to juggling optional types again, for one), but those will have to wait for another post.

Leave a Reply

Your email address will not be published. Required fields are marked *