Slide 1

Slide 1 text

@jacksoncheek Kotlin != Swift

Slide 2

Slide 2 text

Mobile Engineer

Slide 3

Slide 3 text

Android + iOS

Slide 4

Slide 4 text

Kotlin + Swift Bridging the gap between your two codebases

Slide 5

Slide 5 text

@jacksoncheek 1. The basics Outline

Slide 6

Slide 6 text

@jacksoncheek 1. The basics 2. Language feature differences Outline

Slide 7

Slide 7 text

Kotlin == Swift

Slide 8

Slide 8 text

@jacksoncheek http://nilhcem.com/swift-is-like-kotlin/

Slide 9

Slide 9 text

@jacksoncheek let name: String = "Jackson Cheek" print("My name is \(name)") // My name is Jackson Cheek val name: String = "Jackson Cheek” print("My name is $name”) // My name is Jackson Cheek Swift Kotlin

Slide 10

Slide 10 text

@jacksoncheek enum Language { case kotlin case swift case java case objective_c } enum class Language { KOTLIN, SWIFT, JAVA, OBJECTIVE_C } Swift Kotlin

Slide 11

Slide 11 text

@jacksoncheek enum class Language(name: String) { KOTLIN("kotlin"), SWIFT("swift"), JAVA("java"), OBJECTIVE_C("objective-c") } enum Language: String { case kotlin = "kotlin" case swift = "swift" case java = "java" case objective_c = "objective-c" } Swift Kotlin

Slide 12

Slide 12 text

@jacksoncheek protocol Developer { } interface Developer { } Swift Kotlin

Slide 13

Slide 13 text

@jacksoncheek protocol Developer { func languages() -> Set } interface Developer { fun languages(): Set } Swift Kotlin

Slide 14

Slide 14 text

@jacksoncheek class Person { init() { } } class Person( ) { } Swift Kotlin

Slide 15

Slide 15 text

@jacksoncheek class Person { let name: String let languages: Array init(name: String, _ languages: Language...) { self.name = name self.languages = languages } } class Person( val name: String, vararg val languages: Language ) { } Swift Kotlin

Slide 16

Slide 16 text

@jacksoncheek class Person: Developer { let name: String let languages: Array init(name: String, _ languages: Language...) { self.name = name self.languages = languages } } class Person( val name: String, vararg val languages: Language ): Developer { } Swift Kotlin

Slide 17

Slide 17 text

@jacksoncheek class Person: Developer { let name: String let languages: Array init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set { } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set { } } Swift Kotlin

Slide 18

Slide 18 text

@jacksoncheek class Person: Developer { let name: String let languages: Array init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set { var unique: Set = Set() return unique } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set { val unique: Set = setOf() return unique } } Swift Kotlin

Slide 19

Slide 19 text

@jacksoncheek class Person: Developer { let name: String let languages: Array init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set { var unique: Set = Set() for language in _languages { } return unique } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set { val unique: Set = setOf() for (language in languages) { } return unique } } Swift Kotlin

Slide 20

Slide 20 text

@jacksoncheek class Person: Developer { let name: String let languages: Array init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set { var unique: Set = Set() for language in _languages { unique.insert(language) } return unique } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set { val unique: Set = setOf() for (language in languages) { unique.plus(language) } return unique } } Swift Kotlin

Slide 21

Slide 21 text

@jacksoncheek let jackson: Person = Person( name: "Jackson Cheek", .kotlin, .java, .swift ) val jackson: Person = Person( "Jackson Cheek", Language.KOTLIN, Language.JAVA, Language.SWIFT ) Swift Kotlin

Slide 22

Slide 22 text

Kotlin != Swift

Slide 23

Slide 23 text

guard Preference for early return and optional unwrapping

Slide 24

Slide 24 text

@jacksoncheek guard [expression|condition] else { [statement] return } Swift

Slide 25

Slide 25 text

@jacksoncheek var optName: String? = "Jackson Cheek" guard let name = optName else { print("Oops") return } print("My name is \(name)") // My name is Jackson Cheek Swift

Slide 26

Slide 26 text

@jacksoncheek var optName: String? = "Jackson Cheek" guard let name = optName else { print("Oops") return } print("My name is \(name)") // My name is Jackson Cheek var optName: String? = "Jackson Cheek" val name = optName?.let { it } if (name != null) { print("My name is $name") } else { print("Oops") } // My name is Jackson Cheek Swift Kotlin

Slide 27

Slide 27 text

@jacksoncheek var optName: String? = "Jackson Cheek" let age = 30 guard let name = optName, age > 21 else { print("Conditions not met") return } print("Conditions met”) // Conditions met Swift

Slide 28

Slide 28 text

@jacksoncheek var optName: String? = "Jackson Cheek" let age = 30 guard let name = optName, age > 21 else { print("Conditions not met") return } print("Conditions met”) // Conditions met var optName: String? = "Jackson Cheek" val age = 30 val name = optName?.let { it } if (name != null && age > 21) { print("Conditions met") } else { print("Conditions not met") } // Conditions met Swift Kotlin

Slide 29

Slide 29 text

swizzle Changing an implementation at runtime (dangerous)

Slide 30

Slide 30 text

@jacksoncheek • onCreate • onStart • onResume • onPause • onStop • onDestroy

Slide 31

Slide 31 text

@jacksoncheek • viewDidLoad • viewWillAppear • viewDidAppear • viewWillDisappear • viewDidDisappear

Slide 32

Slide 32 text

@jacksoncheek • viewDidLoad • viewWillAppear • viewDidAppear • viewWillDisappear • viewDidDisappear Product Manager: “I want to track every time a user lands on a new screen!”

Slide 33

Slide 33 text

@jacksoncheek @objc func swizzledViewDidAppear() { guard let screenName = className else { return } analyticsService.trackScreen(screenName: screenName) } Swift

Slide 34

Slide 34 text

@jacksoncheek @objc func swizzledViewDidAppear() { guard let screenName = className else { return } analyticsService.trackScreen(screenName: screenName) } class func swizzle() { guard self === UIViewController.self else { return } let original = class_getInstanceMethod( self, #selector(viewDidAppear(_:)) ) } Swift

Slide 35

Slide 35 text

@jacksoncheek @objc func swizzledViewDidAppear() { guard let screenName = className else { return } analyticsService.trackScreen(screenName: screenName) } class func swizzle() { guard self === UIViewController.self else { return } let original = class_getInstanceMethod( self, #selector(viewDidAppear(_:)) ) } #selector Reference to method in compiled code Swift

Slide 36

Slide 36 text

@jacksoncheek @objc func swizzledViewDidAppear() { guard let screenName = className else { return } analyticsService.trackScreen(screenName: screenName) } class func swizzle() { guard self === UIViewController.self else { return } let original = class_getInstanceMethod( self, #selector(viewDidAppear(_:)) ) let swizzled = class_getInstanceMethod( self, #selector(swizzledViewDidAppear) ) } Swift

Slide 37

Slide 37 text

@jacksoncheek @objc func swizzledViewDidAppear() { guard let screenName = className else { return } analyticsService.trackScreen(screenName: screenName) } class func swizzle() { guard self === UIViewController.self else { return } let original = class_getInstanceMethod( self, #selector(viewDidAppear(_:)) ) let swizzled = class_getInstanceMethod( self, #selector(swizzledViewDidAppear) ) method_exchangeImplementations(original!, swizzled!) } Swift

Slide 38

Slide 38 text

retain cycle Just a memory leak

Slide 39

Slide 39 text

@jacksoncheek • No garbage collector Memory Management

Slide 40

Slide 40 text

@jacksoncheek • No garbage collector • Automatic Reference Counting Memory Management

Slide 41

Slide 41 text

@jacksoncheek • No garbage collector • Automatic Reference Counting • Memory allocated for an object when initialized Memory Management

Slide 42

Slide 42 text

@jacksoncheek • No garbage collector • Automatic Reference Counting • Memory allocated for an object when initialized • Memory deallocated when there are no more strong references to that object Memory Management

Slide 43

Slide 43 text

@jacksoncheek • No garbage collector • Automatic Reference Counting • Memory allocated for an object when initialized • Memory deallocated when there are no more strong references to that object • Leak when all references aren’t cleaned up Memory Management

Slide 44

Slide 44 text

@jacksoncheek class Person { let name: String var phone: Phone? = nil init(name: String) { self.name = name } } class Phone { let model: String var owner: Person? = nil init(model: String) { self.model = model } } Swift

Slide 45

Slide 45 text

@jacksoncheek class Person { let name: String var phone: Phone? = nil init(name: String) { self.name = name } } class Phone { let model: String var owner: Person? = nil init(model: String) { self.model = model } } var jackson: Person? = Person(name: "Jackson") var pixel3: Phone? = Phone(model: "Pixel 3") Swift

Slide 46

Slide 46 text

@jacksoncheek class Person { let name: String var phone: Phone? = nil init(name: String) { self.name = name } } class Phone { let model: String var owner: Person? = nil init(model: String) { self.model = model } } var jackson: Person? = Person(name: "Jackson") var pixel3: Phone? = Phone(model: "Pixel 3") jackson!.phone = pixel3 pixel3!.owner = jackson Swift

Slide 47

Slide 47 text

@jacksoncheek var jackson var pixel3 name: “Jackson” phone: nil model: “Pixel 3” owner: nil strong strong 1 1 var jackson: Person? = Person(name: "Jackson") var pixel3: Phone? = Phone(model: "Pixel 3") Swift

Slide 48

Slide 48 text

@jacksoncheek var jackson var pixel3 name: “Jackson” phone: model: “Pixel 3” owner: strong strong strong strong 2 2 var jackson: Person? = Person(name: "Jackson") var pixel3: Phone? = Phone(model: "Pixel 3") jackson!.phone = pixel3 pixel3!.owner = jackson Swift

Slide 49

Slide 49 text

@jacksoncheek var jackson var pixel3 strong strong name: “Jackson” phone: model: “Pixel 3” owner: 1 1 var jackson: Person? = Person(name: "Jackson") var pixel3: Phone? = Phone(model: "Pixel 3") jackson!.phone = pixel3 pixel3!.owner = jackson jackson = nil pixel3 = nil Swift

Slide 50

Slide 50 text

@jacksoncheek class Phone { let model: String weak var owner: Person? = nil init(model: String) { self.model = model } } Swift var jackson var pixel3 name: “Jackson” phone: model: “Pixel 3” owner: strong strong strong strong 2 2

Slide 51

Slide 51 text

@jacksoncheek var jackson var pixel3 name: “Jackson” phone: model: “Pixel 3” owner: strong strong strong weak 1 2 class Phone { let model: String weak var owner: Person? = nil init(model: String) { self.model = model } } Swift

Slide 52

Slide 52 text

@jacksoncheek var jackson var pixel3 name: “Jackson” phone: model: “Pixel 3” owner: strong strong weak 0 2 jackson = nil Swift

Slide 53

Slide 53 text

@jacksoncheek jackson = nil Swift var jackson var pixel3 strong model: “Pixel 3” owner: nil 1

Slide 54

Slide 54 text

@jacksoncheek var jackson var pixel3 model: “Pixel 3” owner: nil 0 jackson = nil pixel3 = nil Swift

Slide 55

Slide 55 text

@jacksoncheek var jackson var pixel3 jackson = nil pixel3 = nil Swift

Slide 56

Slide 56 text

struct Basically an object reference-less data class

Slide 57

Slide 57 text

@jacksoncheek struct State { var isLoading: Bool } data class State( var isLoading: Boolean ) Swift Kotlin

Slide 58

Slide 58 text

@jacksoncheek struct State { var isLoading: Bool } var initial = State(isLoading: false) data class State( var isLoading: Boolean ) var initial = State(isLoading = false) Swift Kotlin

Slide 59

Slide 59 text

@jacksoncheek struct State { var isLoading: Bool } var initial = State(isLoading: false) var copy = initial data class State( var isLoading: Boolean ) var initial = State(isLoading = false) var copy = initial Swift Kotlin

Slide 60

Slide 60 text

@jacksoncheek struct State { var isLoading: Bool } var initial = State(isLoading: false) var copy = initial initial.isLoading = true data class State( var isLoading: Boolean ) var initial = State(isLoading = false) var copy = initial initial.isLoading = true Swift Kotlin

Slide 61

Slide 61 text

@jacksoncheek struct State { var isLoading: Bool } var initial = State(isLoading: false) var copy = initial initial.isLoading = true print("Loading state: \(copy.isLoading)") // Loading state: false data class State( var isLoading: Boolean ) var initial = State(isLoading = false) var copy = initial initial.isLoading = true print("Loading state: ${copy.isLoading}") // Loading state: true Swift Kotlin

Slide 62

Slide 62 text

argument labels Syntactic sugar

Slide 63

Slide 63 text

@jacksoncheek enum AccountType { case credit case debit } class Shopper { func payBill(accountType: AccountType) { print("Thank you!") } } enum class AccountType { CREDIT, DEBIT } class Shopper { fun payBill(accountType: AccountType) { print("Thank you!") } } Swift Kotlin

Slide 64

Slide 64 text

@jacksoncheek enum AccountType { case credit case debit } class Shopper { func payBill(accountType: AccountType) { print("Thank you!") } } let shopper: Shopper = Shopper() shopper.payBill(accountType: .credit) // Thank you! enum class AccountType { CREDIT, DEBIT } class Shopper { fun payBill(accountType: AccountType) { print("Thank you!") } } val shopper: Shopper = Shopper() shopper.payBill(accountType = CREDIT) // Thank you! Swift Kotlin

Slide 65

Slide 65 text

@jacksoncheek enum AccountType { case credit case debit } class Shopper { func payBill(_ accountType: AccountType) { print("Thank you!") } } let shopper: Shopper = Shopper() shopper.payBill(.credit) // Thank you! enum class AccountType { CREDIT, DEBIT } class Shopper { fun payBill(accountType: AccountType) { print("Thank you!") } } val shopper: Shopper = Shopper() shopper.payBill(CREDIT) // Thank you! Swift Kotlin

Slide 66

Slide 66 text

@jacksoncheek enum AccountType { case credit case debit } class Shopper { func payBill(with accountType: AccountType) { print("Thank you!”) } } let shopper: Shopper = Shopper() shopper.payBill(with: .credit) // Thank you! enum class AccountType { CREDIT, DEBIT } class Shopper { fun payBill(with accountType: AccountType) { print("Thank you!") } } val shopper: Shopper = Shopper() shopper.payBill(with = CREDIT) // Not possible! ❌ Swift Kotlin

Slide 67

Slide 67 text

@jacksoncheek Questions?