It’s been a while since my last post, and a lot has changed. One of the more obvious changes to the universe I work in, is the introduction of this programming language called Swift, at WWDC 2014. So more than a year ago.
Enough time to reflect on this ever–changing language, that I am unsure I am up–to–date with: I haven’t thoroughly looked into its 2.1 changes, yet. iA writer says that this post weighs in at almost 1.800 words, so you might want to stop after the first half, because a) the rest is just me bitching, and b) it can’t get much better than an excerpt from an Asterix movie.
There’s a lot of enthusiasm about this language. Initially, I was quite enthusiastic myself, and worked through the entirety of “The Swift Programming Language” by the mid of the week it came out. Just as other programming languages I looked into, this one has influenced the way I write Objective–C(++). And there are a couple of features I would love to see in Objective–C(++).
From that last paragraph, you can probably see that I’m nowhere near ditching the language that inspired the name of this blog. For me, using Swift in production is still an absolute no–go. And that has lead to me writing only little code in it.
One of the changes since my last article, is that I have a new job in which I contribute to a third party framework, and when you hear “framework”, “Swift”, and “3rd party” in one sentence, you can be sure that the rest of the sentence will probably discuss some problem. That isn’t to say that you cannot use that framework from Swift! Quite to the contrary, the team has put in a lot of effort to make the framework have top notch Swift support:
All public facing APIs have been audited for, and properly annotated with nullability and generics.
Any new APIs have been designed with Swift in mind, so that using them from that language feels natural.
The framework itself does not contain a single line of Swift code.
You read that last item right.
Swift’s binary compatibility story is best described by the Swift team itself:
While your app’s runtime compatibility is ensured, the Swift language itself will continue to evolve, and the binary interface will also change. To be safe, all components of your app should be built with the same version of Xcode and the Swift compiler to ensure that they work together.
(Source: https://developer.apple.com/swift/blog/?id=2)
Yes, that is from the 2nd ever post on the official Swift Blog — so it’s one–and–a–half years, and one major language version old.
It is also still completely accurate, and will — for the foreseeable future — remain this way.
So if you’re in the business of licensing frameworks to customers, and your framework contains Swift, you have to offer your customers one version of your framework per public Xcode release — and yes: betas are public, in this regard. Staffing up your customer support department is probably a good idea too, because that’s a lot of versions to choose from, and you can bet on having at least one customer have at least one developer in the team that uses your product be on a different Xcode version than the rest. Or maybe the CI server wasn’t updated in time, or some such similar bullshit no-one ever should have to put up with in the 21st century, ever.
Now, that’s the customer–facing side, and from where I stand, that doesn’t exactly sound like sunshine, and rainbows. <snark>But clearly, the developer side is much better, right?</snark> Casting aside SourceKit quitting unexpectedly (1. by week 2, it’s no longer unexpected, and 2. it’s gotten much better, lately), you will have to fix bugs. In your Swift code. And that’s where the fun begins because this is an evolving language (not bad, per se), that changes from Xcode beta to Xcode beta (well…), in ways that are seldom backwards compatible on the source code level (ugh, fuck).
Trying to support that would be equivalent to find permit A38. (Before following that link: I felt the sudden urge to binge–watch all the Asterix movies right now, so be warned…) So, what you could do, instead, would be to state you support the latest stable Xcode, and its latest beta, so that you only ever have to have 2 branches to fix bugs on in incompatible versions of the language — that’s more manageable, but still quite a bit of overhead.
Still reading? Great! So with that, I think I have thoroughly established why “using Swift in production” is as much of an option to me, as going to the moon is.
Okay, but how about using Swift in production when what you’re building is an app?
Disclaimer: I don’t. So what do I know.
What I do know is what would piss me off:
Compile Times
They’ve gotten better, but they are still bad for every project of non–trivial size.
Compiler Diagnostics
They have gotten drastically better since beta 1 of Xcode 6; they are still not good. Generics, and overloading can make them very misleading.
Xcode Editor Bugs
Objective–C suffers here as well, but from my very subjective perspective it’s probably about an order of magnitude worse in Swift.
No C++ Interoperability
To be honest, this isn’t something I run into a lot, but you miss it once you need it.
Impedance Mismatch
This holds especially for older APIs, but anything that uses the dynamic nature of Objective–C feels weird in Swift. Sometimes, like when you set/hook up your UI in code, this leads to a loss in safety, because where there is only a StringLiteralConvertible and no @selector, there is no -Wselector. So your typo goes unchecked, and you end up wondering why pressing that button doesn’t do any good. (Interface Builder would save your bacon here, and warn you — as it always has.) At other times it just leads to confusion.
Behavioral Differences Between Playground and App/Script
This is almost ironic: This feature is an indispensable tool for learning the Swift language, its (new) features, and experimenting with new APIs, without having to go through the (see first item: now even slower) edit–compile–run cycle. But it is really annoying when you’re trying to wrap your head around some aspect of the language, and you figure out that the reason why you’ve been violently banging your head against the wall for the last couple of hours isn’t that you misunderstood something. But instead, the exact code that does not compile in the playground works flawlessly when you throw it in — say — a Swift CLI script.
If you think this last item was a little long for a list, then allow me to take on the elephant in the room:
From early June to mid September, using Swift in production means having to choose between a rock and a hard place.
The rock
During WWDC’s State of the Union, branch your codebase, and stay up to date with the evolution of the new major version of Swift on your development branch(es). This implies fixing every bug twice during the period where you should adopt new OS/framework features, and adapt to those changes — oh, and also you probably wanted to find a sensible time to take vacation while the sun’s up longer, too, right? (In addition, there will be bugs in the new tools, but that affects any development, so listing it here would be unfair.)
The hard place
You don’t use any of the new language features until the golden master of Xcode version++. You miss out on reporting bugs against the language, the compiler, and the rest of the toolchain at a time where they are most likely to be fixed quickly — in turn, making it less likely that these bugs are actually fixed at the time you rely on Xcode to ship your product. Granted: The more severe the bug, the less likely it is that no–one else will report it. But time is a finite resource, the deadline for shipping a new major version of Xcode is tight, and so teams — especially within Apple — have to prioritize. Radically.
Also, in this scenario you have to migrate your code at a time when it runs on at least 1 completely new device (probably there are 2, maybe even more), that you did not have available for testing during development. Device specific bugs/regressions are a reality of life, only their importance/magnitude varies. (I don’t use, or even own an iPhone 6+, but apparently quite a few apps still don’t support it properly. It has been on sale for a year.) So when the bugs from your app running on previously untested OS/device combinations come in, you have to set aside time to migrate your codebase over. If you are working in a software consultancy/agency (at least in Germany), this is also a very busy time of the year anyways: at the end of the fiscal year, all those unused budgets need to be spent — otherwise they might get cut in the next year (this factoid would be downright funny if it wasn’t so sad…but I digress). That means more clients are looking for someone to build them an app or a new feature for their existing one.
And so what looks like a more conservative, and thoughtful approach initially, turns out to be far from great, too.
This has lead me to believe that if things keep changing the way they did over the last year (especially this summer), using Swift in production is just not very attractive.
It offers a couple of things that I’d love to have available: Even with their bugs, playgrounds, and rich comments would be awesome for documentation alone! The guard statement, and deferred initialization of constants (i.e. let foo; if predicate { foo = a; } else { foo = b; }), are absolutely great! And if you ever had the pleasure to work with e.g. libcrypto, the combination of guard, and defer will make you cry tears of joy — this goes double, if your workplace shuns goto statements.
But no binary compatibility to the point where even building a framework, and your app separately can cause problems? If that sounds promising to you, you might also enjoy a piece of this arsenic cake…