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

C4861b1dfdf3bbb21faec4a1acdf183d?s=128

Ellen Shapiro
PRO

March 15, 2019
Tweet

Transcript

  1. THE TRIUMPHS AND TRIBULATIONS OF KOTLIN/NATIVE IN PRACTICE APPDEVCON |

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

    AMSTERDAM, NL | MARCH 2019 @DESIGNATEDNERD | BAKKENBAECK.NO | JUSTHUM.COM
  3. None
  4. None
  5. None
  6. !

  7. !

  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. THE VIRTUAL MACHINE

  17. None
  18. None
  19. None
  20. None
  21. None
  22. SHAMELESS PLUG! HTTPS://STORE.RAYWENDERLICH.COM PRODUCTS/KOTLIN-APPRENTICE

  23. None
  24. None
  25. None
  26. None
  27. None
  28. !

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

  30. None
  31. None
  32. None
  33. None
  34. None
  35. None
  36. None
  37. None
  38. None
  39. None
  40. None
  41. None
  42. None
  43. None
  44. None
  45. None
  46. PACKAGE THIEF VS. GLITTER BOMB TRAP HTTPS://WWW.YOUTUBE.COM/WATCH?V=XOXHDK-HWUO

  47. None
  48. None
  49. None
  50. None
  51. None
  52. ! "

  53. None
  54. None
  55. WITH WHAT SYSTEMS SHALL I BUILD IT?

  56. None
  57. Raspberry pi

  58. Raspberry pi Server

  59. Raspberry pi Server iOS app

  60. Raspberry pi Server iOS app Android App

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

  62. Works With Kotlin? Raspberry pi ✅ Server ✅ iOS app

    ✅ Android App ✅
  63. HTTP://BAKKENBAECK.NO

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

  65. None
  66. NATIVE VS. MULTI-PLATFORM

  67. apply plugin: 'kotlin-multiplatform'

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

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

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

    projects targeting multiple platforms, // for example JVM + Native + JS apply plugin: 'kotlin-multiplatform'
  71. LEARN TO LOVE (TO HATE)

  72. None
  73. None
  74. PROTIP: USE GRADLE 4.10.1* enableFeaturePreview('GRADLE_METADATA')

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

  76. None
  77. None
  78. GET READY TO CARE ABOUT FOLDER STRUCTURE

  79. None
  80. None
  81. None
  82. FUNCTIONS WITH PER-PLATFORM IMPLEMENTATIONS expect fun

  83. None
  84. None
  85. None
  86. None
  87. None
  88. CALLING CODE FROM BOTH APPS!

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

  90. IOS: MODE

  91. None
  92. None
  93. None
  94. None
  95. None
  96. ! OBJECTIVE-C

  97. None
  98. None
  99. IOS/OBJC GOTCHAS

  100. SWIFT Array != KOTLIN Array

  101. SWIFT Array<T> == KOTLIN List<T>

  102. SWIFT enum != KOTLIN enum class IN OBJC

  103. KOTLIN enum class DayOfWeek { Monday, Tuesday, Wednesday, Thursday, Friday,

    Saturday, Sunday; }
  104. 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") } }
  105. None
  106. 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") }
  107. 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") }
  108. 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") }
  109. DECIDING WHAT TO CENTRALIZE

  110. WHAT DO I REALLY HATE BUILDING TWICE?

  111. TALKING TO THE SERVER

  112. JSON FROM NETWORK IN MODELS ⬅ JSON OUT TO NETWORK

  113. None
  114. None
  115. None
  116. None
  117. None
  118. IosClientEngine.kt https://github.com/ktorio/ktor/blob/master/ ktor-client/ktor-client-ios/darwin/src/io/ ktor/client/engine/ios/IosClientEngine.kt

  119. ANDROID

  120. IOS

  121. IOS

  122. None
  123. ! FREEZING

  124. None
  125. TRY TO AVOID MUTATING VARIABLES

  126. AVOID object FROM IOS

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

  128. KOTLIN interface SecureStorage { fun storeTokenString(token: String) fun clearTokenString() fun

    fetchTokenString(): String? }
  129. 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 } }
  130. 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 } }
  131. 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 } }
  132. 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 } }
  133. 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() } }
  134. 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() } }
  135. 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() } }
  136. 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 } }
  137. None
  138. KotlinX Serialization

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

    )
  140. KOTLIN apply plugin: 'kotlinx-serialization' @Serializable data class UserCredentials( val username:

    String, val password: String )
  141. KOTLIN val creds = UserCredentials("lol@ha.no", "guessme") val string = Json.stringify(UserCredentials.serializer(),

    creds) // String is '{"username":"lol@ha.no","password":"guessme"}' val updatedCreds = Json.parse(UserCredentials.serializer(), string) // updatedCreds.username is "lol@ha.no" // updatedCreds.password is "guessme"
  142. KOTLIN val creds = UserCredentials("lol@ha.no", "guessme") val string = Json.stringify(UserCredentials.serializer(),

    creds) // String is '{"username":"lol@ha.no","password":"guessme"}' val updatedCreds = Json.parse(UserCredentials.serializer(), string) // updatedCreds.username is "lol@ha.no" // updatedCreds.password is "guessme"
  143. KOTLIN val creds = UserCredentials("lol@ha.no", "guessme") val string = Json.stringify(UserCredentials.serializer(),

    creds) // String is '{"username":"lol@ha.no","password":"guessme"}' val updatedCreds = Json.parse(UserCredentials.serializer(), string) // updatedCreds.username is "lol@ha.no" // updatedCreds.password is "guessme"
  144. KOTLIN val creds = UserCredentials("lol@ha.no", "guessme") val string = Json.stringify(UserCredentials.serializer(),

    creds) // String is '{"username":"lol@ha.no","password":"guessme"}' val updatedCreds = Json.parse(UserCredentials.serializer(), string) // updatedCreds.username is "lol@ha.no" // updatedCreds.password is "guessme"
  145. KOTLIN val creds = UserCredentials("lol@ha.no", "guessme") val string = Json.stringify(UserCredentials.serializer(),

    creds) // String is '{"username":"lol@ha.no","password":"guessme"}' val updatedCreds = Json.parse(UserCredentials.serializer(), string) // updatedCreds.username is "lol@ha.no" // updatedCreds.password is "guessme"
  146. FROM CLIENT TO SERVER AND BACK!

  147. LOGIC

  148. MVC!

  149. MVVM!

  150. MVA!*

  151. MVA!* * - ANYTHING OTHER THAN A MODEL OR A

    VIEW FOR THE LOVE OF GOD
  152. MVP!

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

  154. MODEL: VIEW: PRESENTER:

  155. MODEL: data class VIEW: PRESENTER:

  156. MODEL: data class VIEW: interface PRESENTER:

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

  158. VIEW: interface

  159. IOS VIEW: ANDROID VIEW: TESTING VIEW:

  160. IOS VIEW: UIViewController ANDROID VIEW: TESTING VIEW:

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

  162. IOS VIEW: UIViewController ANDROID VIEW: Fragment* TESTING VIEW: *May not

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

    not apply if your name is jake wharton
  164. KOTLIN/NATIVE

  165. KOTLIN/NATIVE

  166. KOTLIN/NATIVE

  167. KOTLIN/NATIVE

  168. KOTLIN/NATIVE

  169. ANDROID

  170. ANDROID

  171. IOS

  172. IOS

  173. TESTING YOUR COMMON CODE

  174. commonTest WORKS IF COROUTINES AREN'T INVOLVED!

  175. None
  176. None
  177. None
  178. None
  179. runBlocking DOESN'T EXIST FOR KOTLIN/NATIVE

  180. ANYTHING WITH COROUTINES MUST BE TESTED ON THE JVM

  181. androidTest WILL ALSO RUN YOUR commonTest TESTS!

  182. None
  183. None
  184. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT

  185. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT (WHICH HAVE LITTLE TO

    NOTHING TO DO WITH KOTLIN)
  186. THINGS YOU'LL LEARN DOING SERVER DEVELOPMENT > Docker

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

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

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

    > ORMs for MySQL > One-way hashing for secure password storage
  190. 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
  191. None
  192. None
  193. None
  194. None
  195. DO WE HAVE TIME FOR A TERRIBLE IDEA?

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

  197. !"# NEXT STEPS

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

  199. !"# NEXT STEPS > Build the prototype box > Get

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

    the Raspberry pi software working > Get the raspberry pi actually unlocking the box
  201. !"# 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
  202. !"# 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
  203. !"# 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?
  204. OBLIGATORY SUMMARY SLIDE!

  205. OBLIGATORY SUMMARY SLIDE! > Kotlin/Native has made great strides in

    the last year
  206. 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
  207. 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
  208. 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
  209. 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
  210. None
  211. 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
  212. LINKS! > My original Kotlin Native sandbox https://github.com/designatednerd/ KotlinNativeTest >

    Package Thief vs. Glitter Bomb Trap https://youtube.com/watch?v=xoxhDk-hwuo