At Daresay we’re big advocates of Swift. We can write more expressive, comprehensible code, and it delivers strict compile time type checking, and runtime safety. With Swift we build more stable products that are easier to maintain and extend. Who doesn’t want a solution like that?
In the main, Swift has been a great language to use for many of our projects, but it has a few limitations that we’ll get to later. We’ve been building products with Swift since its launch four years ago, and while Xcode and the developer tooling have been testing at times, the resulting applications have been very stable. For obvious reasons this tends to make customers in government agencies, banks and other businesses happy.
But what about frameworks?
We have recently developed a complex SDK for a client that is intended to be a commercial distribution, as a binary framework. The SDK provides a method of securely sharing sensitive identity data between devices. The data has to be transferred as quickly as possible and yet work in as many different connectivity situations as possible (WiFi, cell networks, BLE).
After weighing up the options we chose to write the iOS platform SDK in Swift.
Swift, but binary frameworks?
While Swift has quickly become the language of choice for application writing, for frameworks, things are a little more complex. Swift has evolved significantly since its launch: syntax changes, Obj-C API import rules, features added and removed, to name but a few. Thankfully, breaking changes to the language syntax are less frequent now and the Swift core team promise source compatibility going forward. All these developments in the language have prevented the Swift development team from achieving stability of ‘application binary interface’. As a result, all Swift modules in an application bundle (typically the app itself and any frameworks or SDKs it integrates) must be built with the same version of the Swift compiler.
The result of this is that any compiled binary framework a developer distributes will only be compatible with the release of Xcode that it was compiled with. If an integrator wishes to upgrade to a new Xcode release, they will need to obtain a matching build of the framework from the framework developer. This obviously imposes additional burden of integrators on the framework.
After being pledged, but missed in Swift 3.0 and Swift 4.0 releases, ABI Stability is again on the horizon. It is a hard requirement of this year’s Swift 5.0 release. Unfortunately there is an additional step, that of ‘module stability’, which must also be achieved before binary frameworks can be created in a forward compatible manner. Peter Steinberg, of PSPDFKit fame, held a great presentation diving into the technical details of this at dotSwift.
Many frameworks written in Swift are simply distributed as source code. This removes the hard Swift compiler version requirement, but even so this isn’t trouble-free. Thanks to all the source-breaking changes, even source distributed frameworks often break between Swift compiler releases. That said, source compatibility is expected to be much better going forward, making this a pretty safe option.
Some developers, for whom binary distribution is a prerequisite, have simply stuck to Obj-C. Peter Steinberg argues for this in his presentation, and I agree, in the general sense – if you must distribute binary frameworks for iOS, sticking to Obj-C will make life a great deal easier for integrators. In his case (as I understand it…) the PSPDFKit framework has an existing large Obj-C++ (yes, that is a thing) codebase.
But, imagine that you’re beginning development on a new framework, a product that is expected to be built upon for several years and maintained for years after that… would you really want to miss out on all the advantages that Swift brings?
Fun fact: For a long time during development we were tracking mysterious crashes in a Swift app. After further investigation we realised they were caused by the crash reporting framework we were using. Go figure!
Biting the bullet
There are binary framework developers who are working in Swift, and have committed themselves to making new releases for each Swift compiler. From corporate developers such as SAP to indie developers such as Daniel Kennett with CascableCore.
Making releases for every Swift compiler release needn’t be that big a headache, as long as you setup CI build machine instances with each version of Xcode that you intend to support, and watch the builds fall out. Yes, of course you need to QA all these builds, but you have automated unit and integration tests, right?
Since the choice of Swift for binary frameworks affects integrators as well as the framework developers, it’s not a choice to be taken lightly. But binary framework incompatibility won’t last forever. The short-term compatibility headache can be worthwhile for the long-term gains.