Share
Embed
Kotlin != Swift
Two modern languages critical to native mobile development with similar language features and subtle differences. In this talk we’ll quickly refresh the basic syntax structures between Swift and Kotlin, and then explore some of the more important conceptual nuances.
If you find yourself working in two native iOS and Android codebases, then this talk is for you! Guard, swizzle, retain cycles and memory management, struct, argument labels, and whatever else we can fit in between.
jacksoncheek
November 12, 2019
Transcript
67 slides
-
@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 -
@jacksoncheek enum Language { case kotlin case swift case java
case objective_c } enum class Language { KOTLIN, SWIFT, JAVA, OBJECTIVE_C } Swift Kotlin -
@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 -
@jacksoncheek protocol Developer { func languages() -> Set<Language> } interface
Developer { fun languages(): Set<Language> } Swift Kotlin -
@jacksoncheek class Person { init() { } } class Person(
) { } Swift Kotlin -
@jacksoncheek class Person { let name: String let languages: Array<Language>
init(name: String, _ languages: Language...) { self.name = name self.languages = languages } } class Person( val name: String, vararg val languages: Language ) { } Swift Kotlin -
@jacksoncheek class Person: Developer { let name: String let languages:
Array<Language> init(name: String, _ languages: Language...) { self.name = name self.languages = languages } } class Person( val name: String, vararg val languages: Language ): Developer { } Swift Kotlin -
@jacksoncheek class Person: Developer { let name: String let languages:
Array<Language> init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set<Language> { } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set<Language> { } } Swift Kotlin -
@jacksoncheek class Person: Developer { let name: String let languages:
Array<Language> init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set<Language> { var unique: Set<Language> = Set() return unique } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set<Language> { val unique: Set<Language> = setOf() return unique } } Swift Kotlin -
@jacksoncheek class Person: Developer { let name: String let languages:
Array<Language> init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set<Language> { var unique: Set<Language> = Set() for language in _languages { } return unique } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set<Language> { val unique: Set<Language> = setOf() for (language in languages) { } return unique } } Swift Kotlin -
@jacksoncheek class Person: Developer { let name: String let languages:
Array<Language> init(name: String, _ languages: Language...) { self.name = name self.languages = languages } func languages() -> Set<Language> { var unique: Set<Language> = Set() for language in _languages { unique.insert(language) } return unique } } class Person( val name: String, vararg val languages: Language ): Developer { override fun languages(): Set<Language> { val unique: Set<Language> = setOf() for (language in languages) { unique.plus(language) } return unique } } Swift Kotlin -
@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 -
@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 -
@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 -
@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 -
@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 -
@jacksoncheek • onCreate • onStart • onResume • onPause •
onStop • onDestroy -
@jacksoncheek • viewDidLoad • viewWillAppear • viewDidAppear • viewWillDisappear •
viewDidDisappear Product Manager: “I want to track every time a user lands on a new screen!” -
@jacksoncheek @objc func swizzledViewDidAppear() { guard let screenName = className
else { return } analyticsService.trackScreen(screenName: screenName) } Swift -
@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 -
@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 -
@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 -
@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 -
@jacksoncheek • No garbage collector • Automatic Reference Counting •
Memory allocated for an object when initialized Memory Management -
@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 -
@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 -
@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 -
@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 -
@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 -
@jacksoncheek var jackson var pixel3 <Person Instance> name: “Jackson” phone:
nil <Phone Instance> model: “Pixel 3” owner: nil strong strong 1 1 var jackson: Person? = Person(name: "Jackson") var pixel3: Phone? = Phone(model: "Pixel 3") Swift -
@jacksoncheek var jackson var pixel3 <Person Instance> name: “Jackson” phone:
<Phone> <Phone Instance> model: “Pixel 3” owner: <Person> 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 -
@jacksoncheek var jackson var pixel3 strong strong <Person Instance> name:
“Jackson” phone: <Phone> <Phone Instance> model: “Pixel 3” owner: <Person> 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 -
@jacksoncheek class Phone { let model: String weak var owner:
Person? = nil init(model: String) { self.model = model } } Swift var jackson var pixel3 <Person Instance> name: “Jackson” phone: <Phone> <Phone Instance> model: “Pixel 3” owner: <Person> strong strong strong strong 2 2 -
@jacksoncheek var jackson var pixel3 <Person Instance> name: “Jackson” phone:
<Phone> <Phone Instance> model: “Pixel 3” owner: <Person> strong strong strong weak 1 2 class Phone { let model: String weak var owner: Person? = nil init(model: String) { self.model = model } } Swift -
@jacksoncheek var jackson var pixel3 <Person Instance> name: “Jackson” phone:
<Phone> <Phone Instance> model: “Pixel 3” owner: <Person> strong strong weak 0 2 jackson = nil Swift -
@jacksoncheek jackson = nil Swift var jackson var pixel3 strong
<Phone Instance> model: “Pixel 3” owner: nil 1 -
@jacksoncheek var jackson var pixel3 <Phone Instance> model: “Pixel 3”
owner: nil 0 jackson = nil pixel3 = nil Swift -
@jacksoncheek struct State { var isLoading: Bool } data class
State( var isLoading: Boolean ) Swift Kotlin -
@jacksoncheek struct State { var isLoading: Bool } var initial
= State(isLoading: false) data class State( var isLoading: Boolean ) var initial = State(isLoading = false) Swift Kotlin -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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