Cross-platform Swift

Cross-platform Swift

Swift is now not only available on all four Apple platforms, but also on Linux — and possibly soon elsewhere as well. Each platform has some peculiarities, but we want to write at least some of our code in a way that works on all of them, e.g. to share a piece of business logic between our iOS application and the server-side. This talk gives an overview on which APIs are available cross-platform and how we can effectively build components that work everywhere.

9d2ea021919ff81e02d48530aae191bd?s=128

Boris Bügling

June 16, 2016
Tweet

Transcript

  1. 4.
  2. 5.
  3. 9.
  4. 10.

    !

  5. 12.
  6. 14.

    THERE'S MORE DEPENDING ON THE SUBSET WE TARGET Accelerate.framework AudioToolbox.framework

    AudioUnit.framework AVFoundation.framework AVKit.framework CloudKit.framework CoreBluetooth.framework
  7. 15.

    THERE'S MORE DEPENDING ON THE SUBSET WE TARGET CoreImage.framework CoreMedia.framework

    CoreVideo.framework EventKit.framework GameController.framework GameKit.framework GLKit.framework MapKit.framework
  8. 16.

    THERE'S MORE DEPENDING ON THE SUBSET WE TARGET MediaAccessibility.framework Metal.framework

    MobileCoreServices.framework SceneKit.framework SpriteKit.framework StoreKit.framework SystemConfiguration.framework
  9. 18.

    NIBS ! If you don't feel like copy-pasting stuff between

    NIBs ! https://github.com/neonichu/bohne
  10. 20.

    OPEN SOURCE SWIFT ▸ FreeBSD (https://github.com/apple/swift/pull/713) ▸ Windows / Cygwin

    (https://github.com/apple/swift/pull/1108) ▸ Android (https://github.com/apple/swift/pull/1442)
  11. 21.
  12. 23.

    CALLING JAVA USING JNI FROM SWIFT import CJavaVM var vm:

    UnsafeMutablePointer<JavaVM> = 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, [])
  13. 26.

    FOUNDATION IS INCOMPLETE AND SOMETIMES DIFFERENT FROM OS X #if

    os(Linux) let index = p.startIndex.distanceTo(p.startIndex.successor()) path = NSString(string: p).substringFromIndex(index) #else path = p.substringFromIndex(p.startIndex.successor()) #endif
  14. 28.

    SOME THINGS IN THE STANDARD LIBRARY MIGHT NOT BE AVAILABLE

    #if _runtime(_ObjC) // Excluded due to use of dynamic casting and Builtin.autorelease, neither // of which correctly work without the ObjC Runtime right now. // See rdar://problem/18801510 [...] public func getVaList(args: [CVarArgType]) -> CVaListPointer {
  15. 30.

    E.G. STRUCT MEMBERS CAN BE COMPLETELY DIFFERENT let flags =

    GLOB_TILDE | GLOB_BRACE | GLOB_MARK if system_glob(cPattern, flags, nil, &gt) == 0 { #if os(Linux) let matchc = gt.gl_pathc #else let matchc = gt.gl_matchc #endif
  16. 37.

    USING BUILD CONFIGURATIONS #if os(OSX) print("Running on OS X") #elseif

    os(watchOS) print("Running on watchOS") #else print("Running on any platform but OS X and watchOS") #end
  17. 38.

    CAN ALSO BE USED TO DISTINGUISH VERSIONS (SE-0020) #if swift(>=2.2)

    print("Active!") #else this! code! will! not! parse! or! produce! diagnostics! #endif
  18. 39.

    OR TO SEE IF YOUR CODE IS BEING BUILT BY

    SWIFTPM #if SWIFT_PACKAGE import Foundation #endif
  19. 41.

    [XCODE 7 BETA] LINKING DUAL (IPHONEOS AND WATCHOS) FRAMEWORKS WITH

    SAME PRODUCT NAME CAUSES ARCHIVE TO FAIL http://openradar.appspot.com/22392501
  20. 44.

    IF A POD DOES NOT SUPPORT A CERTAIN PLATFORM ▸

    Fork and add it ▸ Submit a PR ▸ For the adventurous ! https://github.com/orta/cocoapods-expert-difficulty
  21. 45.

    COCOAPODS AVOIDS 22392501 ▸ Each duplicated target gets scoped ▸

    <.../Build/Debug-iphoneos/Pods-UserTarget/*.framework>
  22. 46.

    CONCHE ▸ Deprecated now, but nice example of what package

    manifests enable $ conche build Downloading Dependencies -> PathKit 0.5.0 -> Commander 0.5.0 Building Dependencies -> PathKit -> Commander Building Conche Building Entry Points -> conche -> .conche/bin/conche
  23. 48.

    SWIFT PACKAGE MANAGER ▸ Currently the only way to target

    Linux ▸ Does not support iOS, watchOS or tvOS, though
  24. 49.

    SWIFT PACKAGE MANAGER ▸ No real platform syntax, but can

    use build configs: import PackageDescription var package = Package() #if os(Linux) let target = Target(name: "LinuxSources/foo") package.targets.append(target) #endif
  25. 50.

    CURRENT STATUS ▸ Use swiftpm for OS X and Linux

    ▸ Use CocoaPods for the iOS-ish platforms ▸ Use build configurations to distinguish
  26. 53.
  27. 56.
  28. 57.

    DOCKER Easiest way on OS X: dlite https://github.com/nlf/dlite/releases $ brew

    install dlite docker $ sudo dlite install $ docker run -it ubuntu bash
  29. 58.

    DOCKER ▸ A Swift Dockerfile e.g. https://github.com/IBM-MIL/Samples/tree/master/docker- swift $ docker

    build -t swift ./ $ docker run -it swift /bin/bash $ docker ps # Get 'CONTAINER ID' ▸ or docker pull swiftdocker/swift
  30. 59.

    DOCKER If you don't want to create new containers all

    the time: $ docker start XXXX $ docker exec -it XXXX /bin/bash $ docker stop XXXX
  31. 61.

    CI

  32. 62.

    TRAVIS CI os: - linux - osx language: generic sudo:

    required dist: trusty osx_image: xcode7.2 install: - curl -sL https://gist.github.com/kylef/ 5c0475ff02b7c7671d2a/raw/ 621ef9b29bbb852fdfd2e10ed147b321d792c1e4/swiftenv-install.sh | bash script: - . ~/.swiftenv/init
  33. 64.

    EMOJI! ! " # $ % & ' ( )

    * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
  34. 65.

    ELECTRONIC-MOJI import Foundation import Regex extension Character { public var

    unicodeName: String { let mutableString = NSMutableString(string: "\(self)") CFStringTransform(mutableString, nil, kCFStringTransformToUnicodeName, false) let unicodeName = "\(mutableString)".lowercaseString let regex = Regex(".*\\{(.*)\\}") return regex.match(unicodeName)?.captures.first.flatMap { $0 } ?? unicodeName } } extension CollectionType where Generator.Element == Character { public func findUnicodeName(term: String) -> [Character] { let regex = Regex(".*\(term).*") return self.filter { regex.matches($0.unicodeName) } } }
  35. 66.

    FRANK Frank is a DSL for quickly writing web applications

    in Swift with type- safe path routing. https://github.com/nestproject/Frank
  36. 67.

    import Emoji import Frank get { _ in return "Hello

    !\n" } get(*) { (_, name: String) in return (EMOJI.findUnicodeName(name) .map { "\($0)" } .first ?? "¯\\_(ϑ)_/¯") + "\n" }
  37. 68.
  38. 69.

    .build/debug/example [2016-04-26 10:21:09 +0200] [65201] [INFO] Listening at http://0.0.0.0:8000 (65201)

    [2016-04-26 10:21:09 +0200] [65202] [INFO] Booting worker process with pid: 65202 [worker] GET / - 200 OK [worker] GET /glasses - 200 OK [worker] GET /nerd - 200 OK $ curl http://localhost:8000 Hello ! $ curl http://localhost:8000/glasses " $ curl http://localhost:8000/nerd #
  39. 72.

    CONCLUSION ▸ It's complicated :) ▸ Still a lot of

    code can be shared ▸ Swift 3.0 will help a lot
  40. 74.

    REFERENCES ▸ https://swift.org ▸ https://github.com/neonichu/freedom ▸ https://github.com/kylef/swiftenv ▸ https://github.com/nestproject/Frank ▸

    https://speakerdeck.com/jpsim/practical-cross-platform-swift ▸ https://speakerdeck.com/kylef/end-to-end-building-a-web-