Slide 1

Slide 1 text

CROSS-PLATFORM SWIFT MOBOS CONFERENCE, FEBRUARY 2016 BORIS BÜGLING - @NEONACHO

Slide 2

Slide 2 text

COCOAPODS

Slide 3

Slide 3 text

CONTENTFUL

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

AGENDA ▸ Which platforms can we target? ▸ How to share code between them ▸ Some practical examples

Slide 6

Slide 6 text

WHICH PLATFORMS CAN WE TARGET?

Slide 7

Slide 7 text

APPLE PLATFORMS ▸ OS X ▸ iOS ▸ watchOS ▸ tvOS

Slide 8

Slide 8 text

FRAMEWORKS SHARED BETWEEN APPLE PLATFORMS CFNetwork.framework CoreData.framework CoreFoundation.framework CoreGraphics.framework CoreLocation.framework CoreText.framework Foundation.framework ImageIO.framework Security.framework

Slide 9

Slide 9 text

THERE'S MORE DEPENDING ON THE SUBSET WE TARGET Accelerate.framework AudioToolbox.framework AudioUnit.framework AVFoundation.framework AVKit.framework CloudKit.framework CoreBluetooth.framework

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

THERE'S MORE DEPENDING ON THE SUBSET WE TARGET MediaAccessibility.framework Metal.framework MobileCoreServices.framework SceneKit.framework SpriteKit.framework StoreKit.framework SystemConfiguration.framework

Slide 12

Slide 12 text

UIKIT When only targeting iOS and tvOS

Slide 13

Slide 13 text

NIBS ! If you don't feel like copy-pasting stuff between NIBs ! https://github.com/neonichu/bohne

Slide 14

Slide 14 text

OPEN SOURCE SWIFT ▸ Linux

Slide 15

Slide 15 text

OPEN SOURCE SWIFT ▸ FreeBSD (somewhat)

Slide 16

Slide 16 text

OPEN SOURCE SWIFT ▸ Android (someday, maybe) https://github.com/SwiftAndroid/swift

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

APPLE'S GOAL ▸ Be compatible with Swift 3.0 ▸ Scheduled to ship by the end of 2016

Slide 20

Slide 20 text

EVEN 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 {

Slide 21

Slide 21 text

EVEN LIBC IS PROBLEMATIC #if os(Linux) import Glibc #else import Darwin.C #endif

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

AND OTHER RANDOM FUN ./.build/debug/spectre-build /usr/bin/ld: .build/debug/Clock.a(ISO8601Parser.swift.o): undefined reference to symbol '_swift_FORCE_LOAD_$_swiftGlibc' /home/travis/.swiftenv/versions/swift-2.2-SNAPSHOT-2015-12-22-a/ usr/lib/swift/linux/libswiftGlibc.so: error adding symbols: DSO missing from command line clang: error: linker command failed with exit code 1 (use -v to see invocation)

Slide 24

Slide 24 text

=> IT'S COMPLICATED

Slide 25

Slide 25 text

HOW TO SHARE CODE BETWEEN THEM

Slide 26

Slide 26 text

▸ Shared files ▸ Shared frameworks ▸ Shared packages

Slide 27

Slide 27 text

SHARED FILES

Slide 28

Slide 28 text

BUILD CONFIGURATIONS ▸ os(): OSX, iOS, watchOS, tvOS, Linux ▸ arch(): x86_64, arm, arm64, i386

Slide 29

Slide 29 text

#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

Slide 30

Slide 30 text

SHARED FRAMEWORKS

Slide 31

Slide 31 text

[XCODE 7 BETA] LINKING DUAL (IPHONEOS AND WATCHOS) FRAMEWORKS WITH SAME PRODUCT NAME CAUSES ARCHIVE TO FAIL http://openradar.appspot.com/22392501

Slide 32

Slide 32 text

SHARED PACKAGES ▸ CocoaPods ▸ Carthage ▸ Swift Package Manager

Slide 33

Slide 33 text

COCOAPODS "platforms" : { "osx" : "10.10", "ios": "8.0", "watchos": "2.0", "tvos": "9.0" }

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

COCOAPODS AVOIDS 22392501 ▸ Each duplicated target gets scoped ▸ <.../Build/Debug-iphoneos/Pods-UserTarget/ *.framework>

Slide 36

Slide 36 text

CONCHE $ 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

Slide 37

Slide 37 text

CARTHAGE ▸ Essentially a nicer way to do shared frameworks

Slide 38

Slide 38 text

SWIFT PACKAGE MANAGER

Slide 39

Slide 39 text

WHAT IS SWIFTPM?

Slide 40

Slide 40 text

import PackageDescription let package = Package( name: "Hello", dependencies: [ .Package(url: "ssh://[email protected]/Greeter.git", versions: Version(1,0,0)..

Slide 41

Slide 41 text

$ swift build Compiling Swift Module 'Clock' (2 sources) Linking Library: .build/debug/Clock.a

Slide 42

Slide 42 text

WHAT DOES IT DO? ▸ Compiles and links Swift packages ▸ Resolves, fetches and builds their dependencies

Slide 43

Slide 43 text

CURRENT STATE ▸ Currently builds static libraries or binaries ▸ Supported platforms are OS X and Ubuntu Linux ▸ Only builds Swift code, no C/C++/Objective-C/...

Slide 44

Slide 44 text

$ swift build --help OVERVIEW: Build sources into binary products USAGE: swift build [options] MODES: --configuration Build with configuration (debug|release) [-c] --clean Delete all build intermediaries and products [-k] OPTIONS: --chdir Change working directory before any other operation [-C] -v Increase verbosity of informational output

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

MAKING OUR OWN PACKAGE

Slide 47

Slide 47 text

! A small library for parsing and writing ISO8601 date strings.

Slide 48

Slide 48 text

Sources/ !"" Clock #"" ISO8601Parser.swift !"" ISO8601Writer.swift 1 directory, 2 files

Slide 49

Slide 49 text

$ touch Package.swift $ swift build

Slide 50

Slide 50 text

TESTS?

Slide 51

Slide 51 text

SPECTRE describe("a person") { let person = Person(name: "Kyle") $0.it("has a name") { try expect(person.name) == "Kyle" } $0.it("returns the name as description") { try expect(person.description) == "Kyle" } }

Slide 52

Slide 52 text

SPECTRE-BUILD $ swift build $ .build/debug/spectre-build -> a person -> has a name -> returns the name as description 2 passes and 0 failures

Slide 53

Slide 53 text

import PackageDescription let package = Package( name: "Clock", testDependencies: [ .Package(url: "https://github.com/neonichu/spectre-build.git", majorVersion: 0), ] )

Slide 54

Slide 54 text

BUILD_DIR=./.build/debug .PHONY: clean lib test test: lib $(BUILD_DIR)/spectre-build clean: swift build --clean lib: swift build

Slide 55

Slide 55 text

$ make test swift build ./.build/debug/spectre-build -> Converting dates to strings -> can convert NSDate to an ISO8601 GMT string -> Parsing of localtime dates -> can parse dates -> can parse dates with negative timezone offsets -> can parse timezone offsets without colons -> Parsing of UTC dates -> can parse dates -> can parse epoch -> can parse dates without seconds -> is resilient against Y2K bugs 8 passes and 0 failures

Slide 56

Slide 56 text

SWIFT VERSIONS $ cat .swift-version swift-2.2-SNAPSHOT-2015-12-22-a ▸ Is read by either chswift or swiftenv

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

GIT TAGGING ▸ Package.swift only supports tagged dependencies ▸ Don't forget to push your tags to GitHub

Slide 59

Slide 59 text

COCOAPODS ▸ chocolat-cli converts Package.swift to a JSON Podspec

Slide 60

Slide 60 text

public func parse_package(packagePath: String) throws -> PackageDescription.Package { // FIXME: We depend on `chswift` installation and use here let toolchainPath = PathKit.Path(POSIX.getenv("CHSWIFT_TOOLCHAIN") ?? "") libc.setenv("SPM_INSTALL_PATH", toolchainPath.parent().description, 1) print_if("Using libPath \(Resources.runtimeLibPath)", false) let package = (try Manifest(path: packagePath)).package print_if("Converting package \(package.name) at \(packagePath)", false) return package }

Slide 61

Slide 61 text

INTEGRATE SYSTEM LIBRARIES ▸ Empty Package.swift ▸ module.modulemap: module curl [system] { header "/usr/include/curl/curl.h" link "curl" export * }

Slide 62

Slide 62 text

let package = Package( name: "example", dependencies: [ .Package(url: "https://github.com/neonichu/curl", majorVersion: 1) ] )

Slide 63

Slide 63 text

HOW DOES IT WORK?

Slide 64

Slide 64 text

MODULES INSIDE SWIFTPACKAGEMANAGER ▸ OS abstractions: libc, POSIX, sys ▸ Package: PackageDescription ▸ Manifest: dep ▸ Downloading code: swift-get ▸ Building code: swift-build

Slide 65

Slide 65 text

DEPENDENCIES ▸ Can be local or remote Git repositories ▸ Need to be tagged ▸ Will be fetched to ./Packages/ MyPackage-0.0.1

Slide 66

Slide 66 text

ROUGH BUILD PROCESS ▸ PackageDescription generates TOML ▸ dep parses the TOML and can generate YAML ▸ Dependencies are fetched by swift-get ▸ YAML is used as input to llbuild ▸ swift-build calls out to llbuild

Slide 67

Slide 67 text

LLBUILD

Slide 68

Slide 68 text

$ cat .build/debug/Clock.o/llbuild.yaml client: name: swift-build tools: {} targets: "": [] Clock: []

Slide 69

Slide 69 text

commands: : tool: swift-compiler executable: "/usr/bin/swiftc" inputs: ["ISO8601Parser.swift","ISO8601Writer.swift"] outputs: ["","Clock.swiftmodule", "ISO8601Parser.swift.o","ISO8601Writer.swift.o"] module-name: "Clock" module-output-path: "Clock.swiftmodule" is-library: true sources: ["ISO8601Parser.swift","ISO8601Writer.swift"] objects: ["ISO8601Parser.swift.o","ISO8601Writer.swift.o"] import-paths: ["/Users/boris/Projects/Clock/.build/debug"] temps-path: "/Users/boris/Projects/Clock/.build/debug/Clock.o/Clock" other-args: ["-j8","-Onone","-g","-target","x86_64-apple-macosx10.10", "-enable-testing","-sdk", "/.../Developer/SDKs/MacOSX10.11.sdk","-I","/usr/local/include"]

Slide 70

Slide 70 text

: tool: shell inputs: ["","ISO8601Parser.swift.o","ISO8601Writer.swift.o"] outputs: ["","Clock.a"] args: ["/bin/sh","-c","rm -f 'Clock.a'; ar cr 'Clock.a' 'ISO8601Parser.swift.o' 'ISO8601Writer.swift.o'"] description: "Linking Library: .build/debug/Clock.a"

Slide 71

Slide 71 text

ADDITIONAL PACKAGE.SWIFT SYNTAX

Slide 72

Slide 72 text

TARGETS import PackageDescription let package = Package( name: "Example", targets: [ Target( name: "top", dependencies: [.Target(name: "bottom")]), Target( name: "bottom") ] )

Slide 73

Slide 73 text

EXCLUSION let package = Package( name: "Example", exclude: ["tools", "docs", "Sources/libA/images"] )

Slide 74

Slide 74 text

TEST DEPENDENCIES import PackageDescription let package = Package( name: "Hello", testDependencies: [ .Package(url: "ssh://[email protected]/Tester.git", versions: Version(1,0,0)..

Slide 75

Slide 75 text

CUSTOMIZING BUILDS import PackageDescription var package = Package() #if os(Linux) let target = Target(name: "LinuxSources/foo") package.targets.append(target) #endif

Slide 76

Slide 76 text

You should think of it as an alpha code base that hasn't had a release yet. Yes, it is useful for doing some things [...] — Daniel Dunbar

Slide 77

Slide 77 text

SOME PRACTICAL EXAMPLES

Slide 78

Slide 78 text

SPRITEKIT EXAMPLE Can run on OS X, iOS and tvOS

Slide 79

Slide 79 text

CLOCK ~/Clock# make swift build ./.build/debug/spectre-build -> Parsing of localtime dates fatal error: init(forSecondsFromGMT:) is not yet implemented: file Foundation/NSTimeZone.swift, line 69 Illegal instruction InvocationError() Makefile:6: recipe for target 'test' failed make: *** [test] Error 1

Slide 80

Slide 80 text

ELECTRONIC MOJI $ ./.build/debug/electronic-moji car ! " # $ % & ' ( ) * + , - .

Slide 81

Slide 81 text

CONTENTFUL SDK

Slide 82

Slide 82 text

CONCLUSION ▸ It's complicated :) ▸ Still a lot of code can be shared ▸ Swift 3.0 will help a lot

Slide 83

Slide 83 text

REFERENCES ▸ https://swift.org ▸ https://github.com/apple/swift-package-manager ▸ https://github.com/apple/llbuild ▸ https://github.com/neonichu/chocolat ▸ https://github.com/neonichu/chswift ▸ https://github.com/neonichu/freedom

Slide 84

Slide 84 text

THANK YOU!

Slide 85

Slide 85 text

@NeoNacho [email protected] http://buegling.com/talks