Thursday, April 23, 2015

How well does Swift play with iOS7?

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?

Nope! It’s the other way around. Application with Swift code bundles Swift specific standard libs.

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.
  • Pay special attention to optional. I recommend this stackoverflow post for more reading.
  • Same goes when optionally casting.
  • 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.

You can clone the Xcode6.3 code source:
git clone https://github.com/corinnekrych/unified-push-helloworld.git
cd unified-push-helloworld
git checkout ios7.experiment
open HelloWorldSwift.xcodeproj
and run it.

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


Let's take an simple app ChuckNorrisJoke (Yes! Chuck Norris is in the place) from aerogear-ios-cookbook written in Swift and let's use aerogear-ios-http (Swift too) an run the app on iOS7.

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.

git clone https://github.com/corinnekrych/aerogear-ios-cookbook-1
git checkout ios7.support
cd aerogear-ios-cookbook/ChuckNorrisJokes
pod install
open ChuckNorrisJokes.xcworkspace
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.

4 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. The problem with using Cocoapods+Swift library and targeting iOS7 is that you cannot distribute it into AppStore. The validation that happens when you try to upload the archive simply tells you that you cannot use dynamic framework and still support iOS7. There is no easy solution to this, you need to bundle the source into your main target. We smashed into this wall when we tried to release our application Febiofest, with which we wanted to support iOS7 as well, but use Snap library which is written in Swift. It all works well in the dev environment, you can deploy it onto your iOS 7 device, but once you try to publish it, it won't let you.

    ReplyDelete
  3. Hi Tadeas, thanks for the feedback! I've read similar issues with optional linking of dynamic lib. I thought cocoapods will go over tthat :(
    Let me give it a trial and get back to you.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete

Note: Only a member of this blog may post a comment.