On Tuesday at our mobile practice standup, I mentioned that I was in-between projects and looking for something to do. Orta suggested migrating Eidolon, the Artsy bidding kiosk app, to Swift 2.

Our CI is broken anyway, so now is the perfect opportunity to make changes that would break CI. Additionally, Swift 2 seems to have more-or-less stabilized in the latest betas, so we don't expect many gotchas leading up to the GM. Finally, this is an enterprise-distributed app, so we don't have to worry about submitting to the App Store using betas of Xcode.

So Swift 2 it is!

When your boss tells you that you can use the new Swift version.

I didn't think it would take long, but Orta was less optimistic. I knew that we would need to start with the dependencies, which was tricky since I was updating to the latest beta (only one day old at this point). Our Podfile needed some changes, but a lot of this was work I had done before when initially moving Eidolon to CocoaPods frameworks, then to Swift 1.2.

Dependencies are weird. Different libraries take different approaches to Swift changes, so I had to evaluate each one individually. Usually it was a matter of telling CocoaPods to use the branch that the library was using for Swift 2 support. It took about an hour or two, but I got our dependencies working.

When pod update works.

Two of our dependencies, Quick and Nimble, were a breeze to update – even though we were using way out-of-date versions. We're really impressed by the well thought-out foundations of their libraries.

The next thing was getting our own code to work. This was a lot more work than I had anticipated, since the automatic migrator in Xcode didn't work.

When the Xcode migrator fails.

No problem – a lot of the time, Xcode's autosuggest worked fine, like adding labels to function calls. But it doesn't catch everything. It turns out that a few hours of manually changing count(array) to array.count etc was a great way to zone out and enjoy a summer afternoon.

After the low-hanging fruit, it was time to move on to the more... esoteric problems. For example, Swift was getting confused by the ambiguity of the <~ we use for ReactiveCocoa 2.x bindings, vs the <~ operator ReactiveCocoa 3.x uses for bindings. Weird.

I tracked down the problem to precedence. ReactiveCocoa's <~ has a precedence of 93. After I changed ours to match, everything was fine.

When your code compiles but you don't know why.

I noticed a lot of changes surrounding the way Swift handles strings. Apple themselves have discussed changes, which were fine. I tried to use their .isEmpty property where I could, but I often had to test if a string wasn't empty. !str.isEmpty doesn't really sit well with me, so we used str.isEmpty == false.

However, the problem was further compounded by the changes to UITextField, whose text property now returns an optional string. So there was a lot of this code:

if (textField.text ?? "").isEmpty == false { ...

Gross. We've since moved onto something nicer, an experiment with Swift 2's power protocols. The above code can now be written as:

if textField.text.isNotNilNotEmpty

Very neat.

It works!

While Orta and I reviewed the pull request, we noted some things we liked, and some things we didn't like. I really like that UIKit now uses Objective-C generic NSArrays so I don't have to cast so much. I really don't like that libraries, mostly the ones that we maintain, don't use that feature of Objective-C yet. That's now on our todo list.

When you put it on your todo list.

I am amazed at how quickly Swift is changing – as a community, we are still seeing new patterns and methodologies emerge. Not all of them will catch on, of course. But what I'm really excited about is that Swift's engineers are building tools that let us do so much. They aren't making architectural decisions for us, but letting us experiment and discover for ourselves what works and what doesn't. The future of iOS development has never felt more exciting.

Categories: ios, mobile, open source, swift