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.
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.
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.xcodeprojand 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?
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 git checkout ios7.support cd aerogear-ios-cookbook/ChuckNorrisJokes pod install open ChuckNorrisJokes.xcworkspaceRun on iOS7 device or on iOS7 simulator and enjoy chuck Norris humour :)
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.