Save 37% off PRO during our Black Friday Sale! »

Cross-platform Swift

Cross-platform Swift

A talk about writing Swift targeting multiple platforms. Given at App Builders 🇨🇭 2016.

9d2ea021919ff81e02d48530aae191bd?s=128

Boris Bügling

April 26, 2016
Tweet

Transcript

  1. CROSS-PLATFORM SWIFT APP BUILDERS SWIZZLELAND, APRIL 2016 BORIS BÜGLING -

    @NEONACHO
  2. COCOAPODS

  3. CONTENTFUL

  4. None
  5. None
  6. CROSS- PLATFORM SWIFT

  7. WHY SWIFT?

  8. None
  9. !

  10. WHICH PLATFORMS CAN WE TARGET?

  11. APPLE PLATFORMS ▸ macOS ! ▸ iOS " ▸ watchOS

    ⌚ ▸ tvOS $ ▸ carOS %
  12. FRAMEWORKS SHARED BETWEEN APPLE PLATFORMS CFNetwork.framework CoreData.framework CoreFoundation.framework CoreGraphics.framework CoreLocation.framework

    CoreText.framework Foundation.framework ImageIO.framework Security.framework
  13. THERE'S MORE DEPENDING ON THE SUBSET WE TARGET Accelerate.framework AudioToolbox.framework

    AudioUnit.framework AVFoundation.framework AVKit.framework CloudKit.framework CoreBluetooth.framework
  14. 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
  15. THERE'S MORE DEPENDING ON THE SUBSET WE TARGET MediaAccessibility.framework Metal.framework

    MobileCoreServices.framework SceneKit.framework SpriteKit.framework StoreKit.framework SystemConfiguration.framework
  16. UIKIT When only targeting iOS and tvOS

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

    NIBs ! https://github.com/neonichu/bohne
  18. OPEN SOURCE SWIFT ▸ Linux

  19. 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)
  20. None
  21. IMPORTING JNI INTO SWIFT module CJavaVM [system] { header "jni.h"

    link "jvm" export * }
  22. 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, [])
  23. A REALLY LONG WAY TO GO !

  24. FRAMEWORKS SHARED BETWEEN all PLATFORMS Foundation.framework https://github.com/apple/swift-corelibs-foundation

  25. 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
  26. APPLE'S GOAL ▸ Be compatible with Swift 3.0 ▸ Scheduled

    to ship by the end of 2016
  27. 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 {
  28. EVEN LIBC IS PROBLEMATIC #if os(Linux) import Glibc #else import

    Darwin.C #endif
  29. 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
  30. => IT'S COMPLICATED

  31. BUT ONLY RIGHT NOW

  32. HOW TO SHARE CODE BETWEEN THEM

  33. ▸ Shared files ▸ Shared frameworks ▸ Shared packages

  34. SHARED FILES

  35. BUILD CONFIGURATIONS ▸ os(): OSX, iOS, watchOS, tvOS, Linux ▸

    arch(): x86_64, arm, arm64, i386
  36. 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
  37. 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
  38. OR TO SEE IF YOUR CODE IS BEING BUILT BY

    SWIFTPM #if SWIFT_PACKAGE import Foundation #endif
  39. SHARED FRAMEWORKS

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

    SAME PRODUCT NAME CAUSES ARCHIVE TO FAIL http://openradar.appspot.com/22392501
  41. SHARED PACKAGES/LIBRARIES ▸ CocoaPods ▸ Carthage ▸ Swift Package Manager

  42. COCOAPODS "platforms" : { "osx" : "10.10", "ios": "8.0", "watchos":

    "2.0", "tvos": "9.0" }
  43. 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
  44. COCOAPODS AVOIDS 22392501 ▸ Each duplicated target gets scoped ▸

    <.../Build/Debug-iphoneos/Pods-UserTarget/*.framework>
  45. 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
  46. CARTHAGE ▸ Essentially a nicer way to do shared frameworks

  47. SWIFT PACKAGE MANAGER ▸ Currently the only way to target

    Linux ▸ Does not support iOS, watchOS or tvOS, though
  48. 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
  49. CURRENT STATUS ▸ Use swiftpm for OS X and Linux

    ▸ Use CocoaPods for the iOS-ish platforms ▸ Use build configurations to distinguish
  50. DEVELOPMENT ENVIRONMENT

  51. ▸ Xcode ▸ Xcode + Swift Toolchain ▸ Any editor

    + swift build
  52. None
  53. SWIFT VERSIONS $ cat .swift-version swift-2.2-SNAPSHOT-2015-12-22-a ▸ Is read by

    either chswift or swiftenv
  54. HOW TO TEST/DEVELOP FOR LINUX?

  55. IBM SWIFT SANDBOX ▸ No third party dependencies ▸ Limited

    to 65535 characters in a source file
  56. 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
  57. 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
  58. 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
  59. HEROKU ▸ https://github.com/neonichu/swift-buildpack ▸ https://github.com/kylef/heroku-buildpack-swift $ heroku run bash

  60. CI

  61. 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
  62. PRACTICAL EXAMPLE

  63. EMOJI! ! " # $ % & ' ( )

    * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
  64. 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) } } }
  65. FRANK Frank is a DSL for quickly writing web applications

    in Swift with type- safe path routing. https://github.com/nestproject/Frank
  66. import Emoji import Frank get { _ in return "Hello

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

  68. .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://0.0.0.0:8000 Hello ! $ curl http://0.0.0.0:8000/glasses " $ curl http://localhost:8000/nerd #
  69. EMOJI SEARCH KEYBOARD https://github.com/neonichu/emoji-search-keyboard

  70. CLI TOOL $ electronic-moji bird !

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

    code can be shared ▸ Swift 3.0 will help a lot
  72. THANK YOU!

  73. 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-
  74. “Experienced engineer examines comments in a legacy module” — =>

    http://classicprogrammerpaintings.com
  75. @NeoNacho boris@contentful.com http://buegling.com/talks