Looking for

Thursday, April 23, 2015

Swift was created with the Objective-C interoperability in mind. It's easy to get why, Swift playing nicely with Objective-C, was required in order to use existing cocoa API. At first, when trying interoperability, I mostly used Objective-C libs in my Swift apps. But, as I progress in my Swift immersion, I soon write reusable Swift code.

Apple stated it from day one:

you can also use Swift code from Objective-C app

as
Swift applications compile into standard binaries plus some Xcode bundling Swift bits in your app, you can run Swift code on iOS 7.

You can run Swift code in iOS7 BUT there are several paths to drill down…

Do you want to run a Swift app on iOS7?

Let's talk about runtime

How does iOS7 understand Swift? Does iOS7 operating system includes Swift support?

From Colemancda's blog post:
"With Swift, Apple has changed how standard libraries are shipped. With Objective-C, all of the standard libraries, system frameworks, and the runtime itself, were shipped with the OS. With Swift, Apple wanted the ability to quickly deprecate parts of the Swift Standard Library and also add new features. While these changes do break apps at the source code level, it would be a huge problem if shipped apps started to break because the standard library they are linked against has an incompatible API.
Apple’s solution to the problem is to ship a specific version of the standard library with your app."

Besides, reading Swift blog post about Compatibility, I found that this statement is interesting:
"When the binary interface stabilizes in a year or two, the Swift runtime will become part of the host OS and this limitation will no longer exist."

iOS8 brings a shinny new langage support: Swift but, the other correlated important change that happens is the way libraries are packaged. Running Swift on iOS7 also brings the question of how well Swift/Objective-C go together.

Let's talk about Objective-C / Swift impedance

So Swift code can be run even when called from Objective-C. Swift is a strongly type-safe language whereas Objective-C is dynamic by essence. It sometimes brings some blurry runtime behaviour (either crash or nothing happen) to watch out for when writing Swift code that aims to run on both Objective-C and Swift:

Swift pure object are not supported: you need to add @objc or inherit from NSObject if your class is visible from Objective-C.

Don’t use iOS8 api: of course… it seems obvious. But it's easy to forget tough and then you run into runtime exception - I say it from experience :))

Some enum support is available in Objective-C since Swift1.2.

etc... I will go in more details in a later blog post.

An interesting open source library which used the Swift first approach (code written in Swift first but compatible with Objective-C) is Quick. Most of the code is written in Swift some adapters in Objective-C are required when Swift paradigm won't fit (note: Quick and Nimble are DSL for BDD testing, DSL doe uses langage paradigm a lot).

Let's see an example

Here is an experiment I did: Run an HelloWorld app written in Swift on iOS7. That app registers to UnifiedPush Server. For this first experiment, let's just have one application with all the source code bundled together.

To run the app you will need a device with iOS7 installed because push notification can not be run from simulator. Also make sure the UPS instance is live on OpenShift. Alternatively if my OpenShift instance is not running, create your own server following the UPS guide.

Run the app on device. Go to UPS console, login with admin/admin. Go to "send message" right hand tab, and send a message. Your message should be displayed in the list of messages.

Now what about if we want to extract the code related to the UPS registration in an external lib?

Do you want to run Swift libs linked to Swift app on iOS7?

Dynamic framework

Swift libraries can only packaged using dynamic framework (sometimes called cocoa touch framework or embedded framework or bundled framework). Although dynamic frameworks are new to iOS8, they used to be used in OSX though for a while.

With Swift, you can’t package your Swift libs statically because static libs would lead to multiple runtimes in the final executable. We’re back to the point we discussed earlier in …: With Swift evolves quickly and ship its a specific version of the standard library with your app.

So you need to copy/paste your lib source code in your final app?

Cocoapods to the rescue

Or use cocoapods 0.36+ with the use_frameworks! option. I recommend you to read the excellent article from Marius: CocoaPods 0.36 - Framework and Swift Support. Behind the scene, cocoapods ensures all dependant libraries are bundled together with the same set of dylibs, which are embedded into the Frameworks subdirectory of the application bundle.

Using cocoapods brings an easy tooling to support dynamic framework with Swift.

Let's see an example

Originally aerogear-ios-http was designed with minimal deployment target to 8.0, in this experimental branch, I'm going to lower the deployment target to 7.0 and adjust some of the Swift code to fit iOS7.

Run on iOS7 device or on iOS7 simulator and enjoy chuck Norris humour :)

Take away

As we've seen, swift code can run on iOS7 and iOS8 but comes with some compromises:

writing code that comply with both Objective-C and Swift.

dynamic framework packaging. Using cocoapods takes some of the burden away.

last but not least, it certainly requires some extra testing as most of the errors will happen at runtime.

Swift is moving fast, and as we've seen the latest version (Swift 1.2 with iOS that ships with iOS8.3) brings improvement for compatibility with Objective-C (enum case). Interoperability is key to achieve developer's Nirvana of "easy maintenance": write once, deploy on both iOS7 and iOS8.