Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Triumphs and Tribulations of Kotlin/Native In Practice - AppDevCon, Amsterdam, Netherlands - March 2019

The Triumphs and Tribulations of Kotlin/Native In Practice - AppDevCon, Amsterdam, Netherlands - March 2019

First talk about a project I've been working on with K/N!

Abstract:
The promise of write-once-run-everywhere has haunted native mobile developers since the first time someone whispered the words “phone gap”. But what if there was a way to have cross-platform development with a modern, type-safe language? JetBrains is trying to make this happen with Kotlin/Native, which compiles to LLVM bytecode, and can run on iOS, Android, and even the web. Learn more about some of the benefits of working with Kotlin/Native, some of the drawbacks, and a few of the (current) potential dealbreakers when it comes to using this exciting new technology in a real app.

Links:
- Main Project: https://github.com/bakkenbaeck/PorchPirateProtector
- Initial K/N Test: https://github.com/designatednerd/KotlinNativeTest

Ellen Shapiro

March 15, 2019
Tweet

More Decks by Ellen Shapiro

Other Decks in Technology

Transcript

  1. THE TRIUMPHS AND TRIBULATIONS
    OF KOTLIN/NATIVE IN PRACTICE
    APPDEVCON | AMSTERDAM, NL | MARCH 2019
    @DESIGNATEDNERD | BAKKENBAECK.NO | JUSTHUM.COM

    View full-size slide

  2. THE TRIUMPHS AND TRIBULATIONS
    OF KOTLIN/NATIVE IN PRACTICE
    APPDEVCON | AMSTERDAM, NL | MARCH 2019
    @DESIGNATEDNERD | BAKKENBAECK.NO | JUSTHUM.COM

    View full-size slide

  3. THE
    VIRTUAL MACHINE

    View full-size slide

  4. SHAMELESS PLUG!
    HTTPS://STORE.RAYWENDERLICH.COM
    PRODUCTS/KOTLIN-APPRENTICE

    View full-size slide

  5. PACKAGE THIEF
    VS.
    GLITTER BOMB TRAP
    HTTPS://WWW.YOUTUBE.COM/WATCH?V=XOXHDK-HWUO

    View full-size slide

  6. PACKAGE THIEF
    VS.
    GLITTER BOMB TRAP
    HTTPS://WWW.YOUTUBE.COM/WATCH?V=XOXHDK-HWUO

    View full-size slide

  7. WITH WHAT SYSTEMS
    SHALL I BUILD IT?

    View full-size slide

  8. Raspberry pi

    View full-size slide

  9. Raspberry pi
    Server

    View full-size slide

  10. Raspberry pi
    Server
    iOS app

    View full-size slide

  11. Raspberry pi
    Server
    iOS app
    Android App

    View full-size slide

  12. Works with Kotlin?
    Raspberry pi
    Server
    iOS app
    Android App

    View full-size slide

  13. Works With Kotlin?
    Raspberry pi ✅
    Server ✅
    iOS app ✅
    Android App ✅

    View full-size slide

  14. HTTP://BAKKENBAECK.NO

    View full-size slide

  15. HTTPS://GITHUB.COM/BAKKENBAECK/
    PORCH PIRATE PROTECTOR

    View full-size slide

  16. NATIVE VS. MULTI-PLATFORM

    View full-size slide

  17. apply plugin: 'kotlin-multiplatform'

    View full-size slide

  18. apply plugin: 'kotlin-native-platform'
    apply plugin: 'kotlin-multiplatform'

    View full-size slide

  19. // For Native ONLY projects
    apply plugin: 'kotlin-native-platform'
    apply plugin: 'kotlin-multiplatform'

    View full-size slide

  20. // For Native ONLY projects
    apply plugin: 'kotlin-native-platform'
    // For projects targeting multiple platforms,
    // for example JVM + Native + JS
    apply plugin: 'kotlin-multiplatform'

    View full-size slide

  21. LEARN TO LOVE (TO HATE)

    View full-size slide

  22. PROTIP: USE GRADLE 4.10.1*
    enableFeaturePreview('GRADLE_METADATA')

    View full-size slide

  23. PROTIP: USE GRADLE 4.10.1*
    enableFeaturePreview('GRADLE_METADATA')
    *as of March 15, 2018

    View full-size slide

  24. GET READY TO CARE ABOUT
    FOLDER STRUCTURE

    View full-size slide

  25. FUNCTIONS WITH PER-PLATFORM IMPLEMENTATIONS
    expect fun

    View full-size slide

  26. CALLING CODE FROM
    BOTH APPS!

    View full-size slide

  27. ANDROID: EASY MODE
    build.gradle
    SomeFragment.kt

    View full-size slide

  28. !
    OBJECTIVE-C

    View full-size slide

  29. IOS/OBJC GOTCHAS

    View full-size slide

  30. SWIFT Array != KOTLIN Array

    View full-size slide

  31. SWIFT Array == KOTLIN List

    View full-size slide

  32. SWIFT enum != KOTLIN enum class IN OBJC

    View full-size slide

  33. KOTLIN
    enum class DayOfWeek {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday;
    }

    View full-size slide

  34. KOTLIN
    fun printForDay(day: DayOfWeek) {
    when (day) {
    DayOfWeek.Monday -> println("You can fall apart")
    DayOfWeek.Tuesday,
    DayOfWeek.Wednesday -> println("Break my heart")
    DayOfWeek.Thursday -> println("Doesn't even start")
    DayOfWeek.Friday -> println("I'm in love")
    DayOfWeek.Saturday -> println("Wait")
    DayOfWeek.Sunday -> print("Always comes too late")
    }
    }

    View full-size slide

  35. SWIFT
    func printForDay(_ day: DayOfWeek) {
    switch day {
    case .monday:
    print("You can fall apart")
    case .tuesday,
    .wednesday:
    print("Break my heart")
    case .thursday:
    print("Doesn't even start")
    case .friday:
    print("I'm in love")
    case .saturday:
    print("Wait")
    case .sunday:
    print("Always comes too late")
    default:
    fatalError("Not a day")
    }

    View full-size slide

  36. SWIFT
    func printForDay(_ day: DayOfWeek) {
    switch day {
    case .monday:
    print("You can fall apart")
    case .tuesday,
    .wednesday:
    print("Break my heart")
    case .thursday:
    print("Doesn't even start")
    case .friday:
    print("I'm in love")
    case .saturday:
    print("Wait")
    case .sunday:
    print("Always comes too late")
    default:
    fatalError("Not a day")
    }

    View full-size slide

  37. SWIFT
    func printForDay(_ day: DayOfWeek) {
    switch day {
    case .monday:
    print("You can fall apart")
    case .tuesday,
    .wednesday:
    print("Break my heart")
    case .thursday:
    print("Doesn't even start")
    case .friday:
    print("I'm in love")
    case .saturday:
    print("Wait")
    case .sunday:
    print("Always comes too late")
    default: // <-
    ! ! !
    fatalError("Not a day")
    }

    View full-size slide

  38. DECIDING WHAT TO
    CENTRALIZE

    View full-size slide

  39. WHAT DO I REALLY HATE
    BUILDING TWICE?

    View full-size slide

  40. TALKING TO THE SERVER

    View full-size slide

  41. JSON FROM NETWORK IN
    MODELS

    JSON OUT TO NETWORK

    View full-size slide

  42. IosClientEngine.kt
    https://github.com/ktorio/ktor/blob/master/
    ktor-client/ktor-client-ios/darwin/src/io/
    ktor/client/engine/ios/IosClientEngine.kt

    View full-size slide

  43. TRY TO AVOID MUTATING VARIABLES

    View full-size slide

  44. AVOID object FROM IOS

    View full-size slide

  45. USE INTERFACES
    TO MUTATE THINGS IN IOS INSTEAD OF K/N

    View full-size slide

  46. KOTLIN
    interface SecureStorage {
    fun storeTokenString(token: String)
    fun clearTokenString()
    fun fetchTokenString(): String?
    }

    View full-size slide

  47. SWIFT
    @objc class Keychain: NSObject, SecureStorage {
    static let shared = Keychain()
    private override init() {
    super.init()
    }
    //TODO: Actually use keychain
    private var token: String?
    func storeTokenString(token: String) {
    self.token = token
    }
    func clearTokenString() {
    self.token = nil
    }
    func fetchTokenString() -> String? {
    return self.token
    }
    }

    View full-size slide

  48. SWIFT
    @objc class Keychain: NSObject, SecureStorage {
    static let shared = Keychain()
    private override init() {
    super.init()
    }
    //TODO: Actually use keychain
    private var token: String?
    func storeTokenString(token: String) {
    self.token = token
    }
    func clearTokenString() {
    self.token = nil
    }
    func fetchTokenString() -> String? {
    return self.token
    }
    }

    View full-size slide

  49. SWIFT
    @objc class Keychain: NSObject, SecureStorage {
    static let shared = Keychain()
    private override init() {
    super.init()
    }
    //TODO: Actually use keychain
    private var token: String?
    func storeTokenString(token: String) {
    self.token = token
    }
    func clearTokenString() {
    self.token = nil
    }
    func fetchTokenString() -> String? {
    return self.token
    }
    }

    View full-size slide

  50. SWIFT
    @objc class Keychain: NSObject, SecureStorage {
    static let shared = Keychain()
    private override init() {
    super.init()
    }
    //TODO: Actually use keychain
    private var token: String?
    func storeTokenString(token: String) {
    self.token = token
    }
    func clearTokenString() {
    self.token = nil
    }
    func fetchTokenString() -> String? {
    return self.token
    }
    }

    View full-size slide

  51. KOTLIN
    object TokenManager {
    fun currentToken(storage: SecureStorage): UserToken? {
    storage.fetchTokenString()?.let {
    return UserToken(it)
    } ?: return null
    }
    fun storeToken(token: UserToken, storage: SecureStorage) {
    storage.storeTokenString(token.token)
    }
    fun clearToken(storage: SecureStorage) {
    storage.clearTokenString()
    }
    }

    View full-size slide

  52. KOTLIN
    object TokenManager {
    fun currentToken(storage: SecureStorage): UserToken? {
    storage.fetchTokenString()?.let {
    return UserToken(it)
    } ?: return null
    }
    fun storeToken(token: UserToken, storage: SecureStorage) {
    storage.storeTokenString(token.token)
    }
    fun clearToken(storage: SecureStorage) {
    storage.clearTokenString()
    }
    }

    View full-size slide

  53. KOTLIN
    object TokenManager {
    fun currentToken(storage: SecureStorage): UserToken? {
    storage.fetchTokenString()?.let {
    return UserToken(it)
    } ?: return null
    }
    fun storeToken(token: UserToken, storage: SecureStorage) {
    storage.storeTokenString(token.token)
    }
    fun clearToken(storage: SecureStorage) {
    storage.clearTokenString()
    }
    }

    View full-size slide

  54. SWIFT
    @objc class Keychain: NSObject, SecureStorage {
    static let shared = Keychain()
    private override init() {
    super.init()
    }
    //TODO: Actually use keychain
    private var token: String?
    func storeTokenString(token: String) {
    self.token = token
    }
    func clearTokenString() {
    self.token = nil
    }
    func fetchTokenString() -> String? {
    return self.token
    }
    }

    View full-size slide

  55. KotlinX Serialization

    View full-size slide

  56. KOTLIN
    data class UserCredentials(
    val username: String,
    val password: String
    )

    View full-size slide

  57. KOTLIN
    apply plugin: 'kotlinx-serialization'
    @Serializable
    data class UserCredentials(
    val username: String,
    val password: String
    )

    View full-size slide

  58. KOTLIN
    val creds = UserCredentials("[email protected]", "guessme")
    val string = Json.stringify(UserCredentials.serializer(), creds)
    // String is '{"username":"[email protected]","password":"guessme"}'
    val updatedCreds = Json.parse(UserCredentials.serializer(), string)
    // updatedCreds.username is "[email protected]"
    // updatedCreds.password is "guessme"

    View full-size slide

  59. KOTLIN
    val creds = UserCredentials("[email protected]", "guessme")
    val string = Json.stringify(UserCredentials.serializer(), creds)
    // String is '{"username":"[email protected]","password":"guessme"}'
    val updatedCreds = Json.parse(UserCredentials.serializer(), string)
    // updatedCreds.username is "[email protected]"
    // updatedCreds.password is "guessme"

    View full-size slide

  60. KOTLIN
    val creds = UserCredentials("[email protected]", "guessme")
    val string = Json.stringify(UserCredentials.serializer(), creds)
    // String is '{"username":"[email protected]","password":"guessme"}'
    val updatedCreds = Json.parse(UserCredentials.serializer(), string)
    // updatedCreds.username is "[email protected]"
    // updatedCreds.password is "guessme"

    View full-size slide

  61. KOTLIN
    val creds = UserCredentials("[email protected]", "guessme")
    val string = Json.stringify(UserCredentials.serializer(), creds)
    // String is '{"username":"[email protected]","password":"guessme"}'
    val updatedCreds = Json.parse(UserCredentials.serializer(), string)
    // updatedCreds.username is "[email protected]"
    // updatedCreds.password is "guessme"

    View full-size slide

  62. KOTLIN
    val creds = UserCredentials("[email protected]", "guessme")
    val string = Json.stringify(UserCredentials.serializer(), creds)
    // String is '{"username":"[email protected]","password":"guessme"}'
    val updatedCreds = Json.parse(UserCredentials.serializer(), string)
    // updatedCreds.username is "[email protected]"
    // updatedCreds.password is "guessme"

    View full-size slide

  63. FROM CLIENT TO SERVER AND BACK!

    View full-size slide

  64. MVA!*
    * - ANYTHING OTHER THAN A MODEL OR A VIEW FOR THE LOVE OF GOD

    View full-size slide

  65. HTTPS://GITHUB.COM/JETBRAINS/
    KOTLINCONF-APP

    View full-size slide

  66. MODEL:
    VIEW:
    PRESENTER:

    View full-size slide

  67. MODEL: data class
    VIEW:
    PRESENTER:

    View full-size slide

  68. MODEL: data class
    VIEW: interface
    PRESENTER:

    View full-size slide

  69. MODEL: data class
    VIEW: interface
    PRESENTER: class

    View full-size slide

  70. VIEW: interface

    View full-size slide

  71. IOS VIEW:
    ANDROID VIEW:
    TESTING VIEW:

    View full-size slide

  72. IOS VIEW: UIViewController
    ANDROID VIEW:
    TESTING VIEW:

    View full-size slide

  73. IOS VIEW: UIViewController
    ANDROID VIEW: Fragment*
    TESTING VIEW:

    View full-size slide

  74. IOS VIEW: UIViewController
    ANDROID VIEW: Fragment*
    TESTING VIEW:
    *May not apply if your name is jake wharton

    View full-size slide

  75. IOS VIEW: UIViewController
    ANDROID VIEW: Fragment*
    TESTING VIEW: ¯\_(ϑ)_/¯
    *May not apply if your name is jake wharton

    View full-size slide

  76. KOTLIN/NATIVE

    View full-size slide

  77. KOTLIN/NATIVE

    View full-size slide

  78. KOTLIN/NATIVE

    View full-size slide

  79. KOTLIN/NATIVE

    View full-size slide

  80. KOTLIN/NATIVE

    View full-size slide

  81. TESTING YOUR COMMON CODE

    View full-size slide

  82. commonTest
    WORKS IF COROUTINES AREN'T INVOLVED!

    View full-size slide

  83. runBlocking
    DOESN'T EXIST FOR KOTLIN/NATIVE

    View full-size slide

  84. ANYTHING WITH COROUTINES MUST BE TESTED
    ON THE JVM

    View full-size slide

  85. androidTest
    WILL ALSO RUN YOUR commonTest TESTS!

    View full-size slide

  86. THINGS YOU'LL LEARN DOING
    SERVER DEVELOPMENT

    View full-size slide

  87. THINGS YOU'LL LEARN DOING
    SERVER DEVELOPMENT
    (WHICH HAVE LITTLE TO NOTHING TO DO WITH KOTLIN)

    View full-size slide

  88. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT
    > Docker

    View full-size slide

  89. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT
    > Docker
    > MySQL

    View full-size slide

  90. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT
    > Docker
    > MySQL
    > ORMs for MySQL

    View full-size slide

  91. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT
    > Docker
    > MySQL
    > ORMs for MySQL
    > One-way hashing for secure password storage

    View full-size slide

  92. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT
    > Docker
    > MySQL
    > ORMs for MySQL
    > One-way hashing for secure password storage
    > Your computer's exact melting point

    View full-size slide

  93. DO WE HAVE TIME FOR
    A TERRIBLE IDEA?

    View full-size slide

  94. AND NOW, A TERRIBLE IDEA:
    A LIVE DEMO!

    View full-size slide

  95. !"#
    NEXT STEPS

    View full-size slide

  96. !"#
    NEXT STEPS
    > Build the prototype box

    View full-size slide

  97. !"#
    NEXT STEPS
    > Build the prototype box
    > Get the Raspberry pi software working

    View full-size slide

  98. !"#
    NEXT STEPS
    > Build the prototype box
    > Get the Raspberry pi software working
    > Get the raspberry pi actually unlocking the box

    View full-size slide

  99. !"#
    NEXT STEPS
    > Build the prototype box
    > Get the Raspberry pi software working
    > Get the raspberry pi actually unlocking the box
    > Work on sharing design elements

    View full-size slide

  100. !"#
    NEXT STEPS
    > Build the prototype box
    > Get the Raspberry pi software working
    > Get the raspberry pi actually unlocking the box
    > Work on sharing design elements
    > Work on sharing localized strings

    View full-size slide

  101. !"#
    NEXT STEPS
    > Build the prototype box
    > Get the Raspberry pi software working
    > Get the raspberry pi actually unlocking the box
    > Work on sharing design elements
    > Work on sharing localized strings
    > Maybe find someone who knows what they're doing to
    build it for real?

    View full-size slide

  102. OBLIGATORY SUMMARY SLIDE!

    View full-size slide

  103. OBLIGATORY SUMMARY SLIDE!
    > Kotlin/Native has made great strides in the last year

    View full-size slide

  104. OBLIGATORY SUMMARY SLIDE!
    > Kotlin/Native has made great strides in the last year
    > Share logic across iOS, android, and server without
    killing performance or sacrificing type/null safety

    View full-size slide

  105. OBLIGATORY SUMMARY SLIDE!
    > Kotlin/Native has made great strides in the last year
    > Share logic across iOS, android, and server without
    killing performance or sacrificing type/null safety
    > it's still beta, You will run into a lot of brick walls

    View full-size slide

  106. OBLIGATORY SUMMARY SLIDE!
    > Kotlin/Native has made great strides in the last year
    > Share logic across iOS, android, and server without
    killing performance or sacrificing type/null safety
    > it's still beta, You will run into a lot of brick walls
    > Ktor and kotlinX serialization are a huge help, if you
    can get around what a mess coroutines can be on iOS

    View full-size slide

  107. OBLIGATORY SUMMARY SLIDE!
    > Kotlin/Native has made great strides in the last year
    > Share logic across iOS, android, and server without
    killing performance or sacrificing type/null safety
    > it's still beta, You will run into a lot of brick walls
    > Ktor and kotlinX serialization are a huge help, if you
    can get around what a mess coroutines can be on iOS
    > This is experimental, but it's stupid amounts of fun

    View full-size slide

  108. LINKS!
    > Kotlin MPP for iOS + Android tutorial
    https://kotlinlang.org/docs/tutorials/
    native/mpp-ios-android.html
    > KotlinConf App
    https://github.com/JetBrains/kotlinconf-
    app
    > KotlinLang Slack
    https://slack.kotlinlang.org

    View full-size slide

  109. LINKS!
    > My original Kotlin Native sandbox
    https://github.com/designatednerd/
    KotlinNativeTest
    > Package Thief vs. Glitter Bomb Trap
    https://youtube.com/watch?v=xoxhDk-hwuo

    View full-size slide