Why do we even want to write cross-platform Swift? In the introduction to Swift slide where Chris Lattner was saying this is basically a classification of languages in terms of developer productivity and app performance, and the goal for Swift is to be in the top right spot where nothing really is at the moment.

You either have things that are a little bit harder to use, but really performant or you have really easy-to-use languages which are rather on the slow side.

Which platforms can be targeted?

All the Apple platforms can be targeted using Swift: Mac OS, iOS, watchOS, tvOS, carOS. This is a list that’s shared between all four Apple platforms.

  • CFNetwork.framework
  • CoreData.framework
  • CoreFoundation.framework
  • CoreGraphics.framework
  • CoreLocation.framework
  • CoreText.framework
  • Foundation.framework
  • ImageIO.framework
  • Security.framework

And then there’s a lot more frameworks you can use if you target the iOS-ish platforms, so iOS, watchOS, and tvOS.

  • Accelerate.framework
  • AudioToolbox.framework
  • AudioUnit.framework
  • AVFoundation.framework
  • AVKit.framework
  • CloudKit.framework
  • CoreBluetooth.framework
  • CoreImage.framework
  • CoreMedia.framework
  • CoreVideo.framework
  • EventKit.framework
  • GameController.framework
  • GameKit.framework
  • GLKit.framework
  • MapKit.framework
  • MediaAccessibility.framework
  • Metal.framework
  • MobileCoreServices.framework
  • SceneKit.framework
  • SpriteKit.framework
  • StoreKit.framework
  • SystemConfiguration.framework

And you can also use UIKit and that kind of cross platform case when targeting iOS and tvOS. An unfortunate thing is you can actually not share nibs between tvOS and iOS. But there are some third party tools to convert your storyboard or your xib from iOS to tvOS. Of course the layout will need to be adjusted and stuff like this but you don’t have to start from scratch.

Open-Source Swift

Open-source Swift targeted Linux at the beginning, but now there is a number of new platforms also which have been added, like freebsd. There’s Windows with cygwin port and there’s Android. A few more words about Android. What can you actually practically do with it? Unfortunately not that much at this point because there’s no frameworks, so you can’t access any of the Android build in things.

There’s not even foundation yet, as far as I know. A venue that might work for doing actually some more stuff there is using JNI in Swift. I played around a little bit with it on macOS, I haven’t yet done it on Android. So you can use module map for the JNI API. It’s a Java driven interface; an API for calling Java code from C normally, but because all C libraries work inside Swift, we can just create this module map for the JNI and then we can actually call the Java from Swift.

Importing JNI into Swift

module CJavaVM [system] {
  header "jni.h"
  link "jvm"
  export *

Calling Java using JNI from Swift

import CJavaVM

var vm: UnsafeMutablePointer = nil
let env = create_vm(&vm)
let jni = env.pointee.pointee

let hello_class = jni.FindClass(env, "helloWorld")

let main_method = jni.GetStaticMethodID(env, hello_class,"main", "([Ljava/lang/String;)V")
jni.CallStaticVoidMethodA(env, hello_class, main_method, [])

It looks actually much better than the C version of this because we don’t have to write all the very long type names that JNI has. And then we can call methods and instantiate classes. You can basically do anything you can do with a JVM you can do with a JNI. It’s a little bit tedious interface, but it should be possible to use this for calling Android native functionality. But there’s a really long way to go, so someone needs to actually make it practical so that not every app needs to basically reimplement this bridging layer.

Foundation is Shared Across All Platforms

One framework that is shared between all of the open source platforms is Foundation. Apple is rewriting it in the Swift calling Foundation project which was part of the Swift open source release. To note it’s incomplete and sometimes it’s different from macOS. Some things are even not available on non-Apple platforms, and that is because the Objective-C runtime doesn’t exist on anything but the Apple platforms and some Swift features depend on the Objective-C runtime.

Any functionality that uses auto release is not available because there’s no auto release in native Swift. An example that was surprising for me is the get b a list function. Where you actually get a pointer that is auto released and not available if you don’t have Objective-C runtime.

But even using the libc is problematic because libc is different. There is a common subset of libc that works everywhere, but there’s many extensions which only work on certain platforms.

How to Share Code Between Platforms

There’s basically three methods we can use. We can share files between targets, we can use shared frameworks, or we can use shared packages.

Shared files

Shared files, that’s pretty simple, you probably all have done it. If you create a new file or if you look at the inspector of a file, we can check the targets where this file is available inside Xcode. And we can use the Swift build configurations like os and arch to select specific code path, which only run on one OS or even a specific architecture.

Shared frameworks

If we have a framework target inside Xcode, we can actually just change the supporter platforms, and let it build four more than one. For example, in this case for OS for iPhone and Apple TV. A big problem with shared frameworks is that they don’t work so well in Xcode still. So when you have one framework that uses the same product name, or cross different architectures you can have problems with, for example, archiving your build.

Shared packages libraries

There’s couple of options there you can use. There’s CocoaPods, Carthage, and now also the Swift package manager. In CocoaPods we have these platforms attribute, where you can actually specify which platform a pod should be built for, which is pretty simple. This is a way to, if you’re just prototyping something, I think a lot of pods are still not supporting tvOS or watchOS necessarily even they would work.

What’s nice about using CocoaPods is other tools can use and build on top of package manifests. For example, Kyle Fuller, made a build tool called conche before Swift PM was announced to, as in preparation of Swift on the Nooks and it was able to build CocoaPods packages on the Nooks in a more limited fashion, but basically it worked and this was possible because we have a package manifest that is just a json file. And that can be used anywhere.

Carthage, is essentially a nicer way to do shared frameworks. We can version them so it doesn’t really differ too much from that approach. You may or may not still run into issues when you have the same framework name shared between different targets, but that depends on how you organize your project.

Swift Package Manager is the only way to target Linux. So it’s not optional if you want to do that. It doesn’t support any of the newer platforms, so no iOS, watchOS, or tvOS. And it doesn’t really have a platform syntax at the moment. If you run Swift Package Manager on macOS, you build for macOS, and if you build for Linux, you build for Linux, which means there’s no way to specify even if something works for a specific platform.

Continuous Integration

I’m using Travis CI for my packages and this is a Travis file you can basically use for anything because it uses swiftenv. The one that is basically specified in the Swift version file that I mentioned earlier, so if you are using that you can just always use the same Travis configuration and basically to get a sub up for testing Linux and macOS with one build.

Alex Goncharov, iOS Lead

iOS Lead

View posts by

Talk to Us