Stupid Swift Tricks #1

It’s been quiet for a while here at Wooji Juice: a few small app updates (Mitosynth and Hokusai both have updates currently either in testing or App Store review), but most of the work is happening behind-the-scenes on R&D.

That includes getting to grips with Swift, Apple’s new programming language. It came out back in June at WWDC, and has seen some updates since. So how is it?

Weeelllll… “It’s really nice, but…”

As a language, I like it a lot. Mostly. There are a few things missing that would be really helpful, but, on the whole the language itself is pretty solid.

The too— SourceKitServer Crashed —ls, on the other hand, are sti— SourceKitServer Crashed —ll in a bit of a state. Various bits of the compi— SourceKitServer Crashed —ler toolchain have bugs in them, which means that either:

  • The parser crashes, especially when run in the background by Xcode as you’re editing (in order to provide syntax highlighting, code completion, symbol navigation, etc). This causes the SourceKitServer error message to flicker on your screen, often rapidly strobing for a few seconds. And, of course, your syntax highlighting and other context-aware IDE tools may stop working, until the server can get back on its feet.
  • The compiler crashes during actual builds, typically with segfault 11, while compiling valid code.
  • The compiler works, but then you get peculiar crashes in MergeSwiftModule
  • Or, it might just freak out about apparently-valid syntax from time to time.

I will say this: for me personally, these issues have manifested themselves at parse/compile time — not at runtime. Which is definitely the way round you want it to be!

The other main problem is that Swift doesn’t currently do incremental compilation. So, if you change a single character of a single file, the entire universe needs to be recompiled in full.

Update: Incremental compilation has been added in Swift 1.2!

AutoLayout For Types

A third issue, thankfully not so critical, is that (as is often the way with type-inference & generics — this isn’t just a Swift thing), the error messages are profoundly unhelpful.

See, the funny thing is, type inference is kinda analogous to AutoLayout — and runs into the same problems.

In AutoLayout, you set up a series of constraints on the frames of UI widgets, and then the layout engine looks upon that as a set of simultaneous equations, and attempts to find a single unambiguous solution that complies with all your constraints. When it works, it’s like magic. However, if there are several solutions (or none), it basically flips a table, throws up its arms and storms out of the room without explaining much about what’s on its mind (often taking a bunch of your widgets with it).

In Swift, you set up a series of constraints on types, and then the compiler looks upon that as a set of simultaneous equations, and attempts to find a single unambiguous solution that complies with all your constraints. When it works, it’s like magic. However, if there are several solutions (or none), it… you get the picture.

I mean, if you supply the wrong type of argument to a regular function, fine: it can say, “This is the wrong type”. But if you supply the wrong type of argument to a generic function, or if it’s trying to deduce the type of a closure, or in a number of other similar situations, often all it can say is something like Cannot convert the expression's type '(($T2) -> ($T2) -> $T1) -> (($T2) -> $T1) -> $T1' to type 'Bool'

It knows the types don’t match, but since there are multiple potential solutions, it doesn’t know which is the correct one that it should be comparing your code against, so it can’t actually tell what you’ve done wrong. All the types are still floating about in quantum superposition.

The trick in this case is generally to either make the types more explicit so there’s less to infer, or pull subexpressions out into a separate let statement and examine it by using Xcode’s display of the inferred type, and repeat — until either Swift gives a concrete error message you can act upon, or Xcode tells you an inferred type you weren’t expecting, and then you know where you went wrong.

(Actually, the trick in the example error message I gave above, is this: it occurs when you have a single-line closure, and assign a value to an Optional within it, eg UIView.animateWithDuration(0.5) { someWidget?.enabled = true }. This works fine if someWidget is not an Optional type, or if there are multiple statements inside the closure. But when there’s only one statement, Swift has to consider the possibility it might be a return value. And apparently being Optional is enough to throw the compiler into existential doubt. The solution? { someWidget?.enabled = true; () } — the no-op/void statement tacked on the end is enough to soothe its fears and return some semblance of ontological stability to its worldview.)

Update: Error messages are also improved in Swift 1.2, but the techniques above can still be useful.

And yet…

Despite these teething troubles, I’ve been sticking with Swift so far. Why? Because the language itself is really very nice to work with. It feels a lot like working in Python, except that it’s compiled and run directly on the hardware, rather than run as bytecode in a virtual machine.

In other words, it runs (for the most part — occasional bugs notwithstanding) at the speed of a low-level language like C or C++, despite having the feel of a high-level language. Plus, a lot of potential run-time errors are found at compile-time. It’s almost like having your cake and eating it.

A lot of this is down to how its protocols, generics and language syntax interact. It gives the feel of duck typing, and the ability to quickly plumb small pieces together in different ways, while actually being strongly typed.

As a result, I’ve found myself putting together a bunch of little reusable fragments as I go along. I thought I might share some from time to time. None of them are earth-shattering, but they’re either nice to have around, or stupid Swift tricks — you can decide which in each case :P

TypeTransformer

So here’s the first little toy I’ve been playing with:

Swift

TypeTransformer
class TypeTransformer<A, B> { let forward: A->B let backward: B->A init(_ forward: A->B, _ backward: B->A) { self.forward = forward self.backward = backward } func convert(source: A) -> B { return forward(source) } func convert(source: B) -> A { return backward(source) } }

This is a class that performs bi-directional conversion between two different types. Say, from String to Int and back again, by supplying it with appropriate closures or functions:

Swift

Example
let transform1 = TypeTransformer({ $0.toInt() ?? 0 }, { "\($0)" }) let a = transform1.convert(42) // "42" let b = transform1.convert("42") // 42

The interesting thing is, we didn’t need to tell it anything about the types here. From context, it always knows which conversion is which, and when it’s appropriate to call each one.

Put another way, the forward/backward parameters are marked as unnamed because it doesn’t actually matter! Pass the closure in the opposite order, no ducks are given:

Swift

elpmaxE
let transform2 = TypeTransformer({ "\($0)" }, { $0.toInt() ?? 0 }) let a = transform2.convert(42) // "42" let b = transform2.convert("42") // 42

We can go further, though. Here’s an extension that can be useful:

Swift

TypeTransformer Extension
extension TypeTransformer { func reverse() -> TypeTransformer<B, A> { return TypeTransformer<B, A>(backward, forward) } }

This lets us explicitly reverse the sense of the TypeTransformer. Why would we do this, given that it already seamlessly worked in either direction? Well, if we want to pass it into a function, that function needs to declare a type for the transformer, and that forces the types to be in a particular ordering:

Swift

Pointless Example
func pointlessFunction(transform: TypeTransformer<String, Int>) { } // This works pointlessFunction(transform1) // This does not pointlessFunction(transform2) // But this fixes it pointlessFunction(transform2.reverse())

It’s kind-of annoying having to figure out if you need to reverse the transformer or not. Let’s fix that:

Swift

Another TypeTransformer Extension
extension TypeTransformer { func magic() -> TypeTransformer<B, A> { return reverse() } func magic() -> TypeTransformer<A, B> { return self } } // Both of these work: the compiler figures it all out for you. pointlessFunction(transform1.magic()) pointlessFunction(transform2.magic())

I haven’t yet been able to figure out a way to get this to happen automatically, so you don’t have to type out .magic() to get it to figure it out. The closest you can get, is to simply overload pointlessFunction() to do the reversal:

Swift

Tedious Workaround
func pointlessFunction(transform: TypeTransformer<Int, String>) { pointlessFunction(transform.reverse()) } // Victory pointlessFunction(transform1) pointlessFunction(transform2)

It’s very simple to write, but it’s annoying that you have to in the first place — it doesn’t really scale to a lot of function definitions. So, if you can figure out a way to make this work automagically, please let me know.

Still, for the moment, this is the approach I’m actually taking, since all this TypeTransformer nonsense is for an API I’m toying with, and as it happens, all the various generic possibilities funnel down to only one function where I actually have to manually reverse the transform. Code using the API can simply provide two transform closures, and as long as they’re a matched pair, it Just Works™.

Fair warning: Although the code above works just fine, while playing about with this stuff, during some of my earlier experiments along the way, I did manage to wedge a Swift Playground so badly that I couldn’t even open Activity Monitor to shut it down, and trying to open a new terminal session failed because there weren’t enough system resourcesfootnote 1, and I ended up having to kill -9 the OS X window server in order to even be able to restart the machine. While making the Elder Sign, and backing away slowly.

Fun times!

1. On a Retina iMac w/ 32GB