I Walk The Line - What Should Be Kotlin/Native And What (Maybe) Shouldn't - KotlinConf, Copenhagen, Denmark, December 2019

C4861b1dfdf3bbb21faec4a1acdf183d?s=47 Ellen Shapiro
December 06, 2019

I Walk The Line - What Should Be Kotlin/Native And What (Maybe) Shouldn't - KotlinConf, Copenhagen, Denmark, December 2019

Code:
https://github.com/bakkenbaeck/PorchPirateProtector

Abstract:
Kotlin/Native is an incredibly powerful toolset for creating cross-platform code, particularly for mobile apps. But iOS and Android both have their...quirks, particularly around handling Application, View Controller, and Fragment/Activity lifecycle. The line between getting things done more efficiently and getting sucked into a whirlpool of memory leaks can seem mighty thin, but it can be walked. In this talk, Ellen will discuss how she's walked it, what's worked -- and what definitely has not.

C4861b1dfdf3bbb21faec4a1acdf183d?s=128

Ellen Shapiro

December 06, 2019
Tweet

Transcript

  1. 1.

    I WALK THE LINE WHAT SHOULD BE KOTLIN/NATIVE, AND WHAT

    (MAYBE) SHOULDN'T KOTLINCONF | COPENHAGEN, DENMARK | DECEMBER 2019 ELLEN SHAPIRO | @DESIGNATEDNERD | APOLLOGRAPHQL.COM
  2. 2.
  3. 3.
  4. 4.
  5. 5.
  6. 6.
  7. 7.

    !

  8. 8.
  9. 11.
  10. 12.
  11. 14.
  12. 15.
  13. 16.
  14. 17.
  15. 18.
  16. 19.
  17. 21.
  18. 22.
  19. 23.
  20. 26.
  21. 28.
  22. 29.
  23. 30.
  24. 31.
  25. 32.
  26. 33.
  27. 34.
  28. 35.
  29. 36.
  30. 37.
  31. 38.
  32. 41.
  33. 42.
  34. 43.

    !

  35. 44.
  36. 47.
  37. 48.
  38. 49.
  39. 55.

    Works With Kotlin? iOS app ✅ Android app ✅ Server

    ✅ Raspberry pi* ✅ * - soon, I hope
  40. 56.
  41. 57.
  42. 58.
  43. 59.
  44. 60.

    MVP

  45. 61.
  46. 62.
  47. 72.
  48. 73.

    commonMain PRESENTER class DeviceDetailPresenter( val view: DeviceDetailView, val device: PairedDevice,

    storage: SecureStorage ): BaseCoroutinePresenter(secureStorage = storage) { // ... }
  49. 74.

    commonMain VIEW interface DeviceDetailView: IndefiniteLoadingIndicating { fun setTitle(toString: String) fun

    setLockButtonEnabled(enabled: Boolean) fun setUnlockButtonEnabled(enabled: Boolean) fun setApiError(toString: String?) }
  50. 75.

    ANDROID APP FRAGMENT class DeviceDetailFragment: Fragment(), DeviceDetailView { private val

    presenter by lazy { DeviceDetailPresenter( this, currentDevice, KeyStoreManager(this.context!!) ) } //... }
  51. 76.

    WHAT HAVE I DONE WRONG HERE? class DeviceDetailFragment: Fragment(), DeviceDetailView

    { private val presenter by lazy { DeviceDetailPresenter( this, currentDevice, KeyStoreManager(this.context!!) ) } //... }
  52. 77.

    WHAT HAVE I DONE WRONG HERE? class DeviceDetailFragment: Fragment(), DeviceDetailView

    { private val presenter by lazy { DeviceDetailPresenter( this, currentDevice, KeyStoreManager(this.context!!) ) } //... }
  53. 78.

    PRESENTER class DeviceDetailPresenter( val view: DeviceDetailView, val device: PairedDevice, storage:

    SecureStorage ): BaseCoroutinePresenter(secureStorage = storage) { // ... }
  54. 79.
  55. 80.
  56. 81.
  57. 83.

    MVP

  58. 84.
  59. 87.

    class DeviceDetailPresenter( val device: PairedDevice ): BaseCoroutinePresenter() { data class

    DeviceDetailViewState( val lockButtonEnabled: Boolean, val unlockButtonEnabled: Boolean, val errorMessage: String? = null, val indicatorAnimating: Boolean = false ) // ... }
  60. 88.

    suspend fun getStatusAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ):

    DeviceDetailViewState { // ... } suspend fun lockAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ): DeviceDetailViewState { //... } suspend fun unlockAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ): DeviceDetailViewState { //.. }
  61. 89.

    suspend fun getStatusAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ):

    DeviceDetailViewState { // ... } suspend fun lockAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ): DeviceDetailViewState { //... } suspend fun unlockAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ): DeviceDetailViewState { //.. }
  62. 90.

    suspend fun getStatusAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ):

    DeviceDetailViewState { // ... } suspend fun lockAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ): DeviceDetailViewState { //... } suspend fun unlockAsync( initialViewStateHandler: (DeviceDetailViewState) -> Unit, secureStorage: SecureStorage ): DeviceDetailViewState { //.. }
  63. 94.
  64. 95.

    import SwiftUI struct ValueView: View { var value: Double =

    0 var body: some View { return Text("\(value)") } }
  65. 96.

    import SwiftUI struct ValueView: View { var value: Double =

    0 var body: some View { return Text("\(value)") } }
  66. 97.

    import SwiftUI struct ValueView: View { var value: Double =

    0 var body: some View { return Text("\(value)") } }
  67. 101.
  68. 102.

    val colorPrimary = "#05C1FD" val colorPrimaryDark = "#5848F4" val colorAccent

    = "#fb5502" val colorAccentSelected = "#c94402" val textLight = "#ffffff" val textMiddle = "#777777" val textDark = "#000000" val errorRed = "#cc0000" val success = "#0d9b2a"
  69. 103.

    enum class PPPColor(val hexColor: String) { ColorPrimary("#05C1FD"), ColorPrimaryDark("#5848F4"), ColorAccent("#fb5502"), ColorAccentSelected("#c94402"),

    TextLight("#ffffff"), TextMiddle("#777777"), TextDark("#000000"), ErrorRed("#cc0000"), Success("#0d9b2a"); val red: Long get() = hexColor.substring(1, 3).toLong(16) val green: Long get() = hexColor.substring(3, 5).toLong(16) val blue: Long get() = hexColor.substring(5, 7).toLong(16) }
  70. 104.

    enum class PPPColor(val hexColor: String) { ColorPrimary("#05C1FD"), ColorPrimaryDark("#5848F4"), ColorAccent("#fb5502"), ColorAccentSelected("#c94402"),

    TextLight("#ffffff"), TextMiddle("#777777"), TextDark("#000000"), ErrorRed("#cc0000"), Success("#0d9b2a"); val red: Long get() = hexColor.substring(1, 3).toLong(16) val green: Long get() = hexColor.substring(3, 5).toLong(16) val blue: Long get() = hexColor.substring(5, 7).toLong(16) }
  71. 105.

    import platform.UIKit.UIColor fun PPPColor.toUIColor(): UIColor { return UIColor( red =

    this.red.toDouble() / 255.0f, green = this.green.toDouble() / 255.0f, blue = this.blue.toDouble() / 255.0f, alpha = 1.0 ) }
  72. 107.
  73. 109.
  74. 110.
  75. 111.
  76. 112.
  77. 113.
  78. 114.
  79. 115.
  80. 116.
  81. 117.
  82. 124.
  83. 125.
  84. 127.
  85. 133.
  86. 134.
  87. 137.
  88. 138.
  89. 139.
  90. 141.
  91. 147.
  92. 148.

    OBLIGATORY SUMMARY SLIDE > Kotlin/Native can be a double-edged sword

    > You can move way faster on some things > Be mindful of the trade-offs you're making
  93. 149.

    OBLIGATORY SUMMARY SLIDE > Kotlin/Native can be a double-edged sword

    > You can move way faster on some things > Be mindful of the trade-offs you're making > Prioritize developer experience to drive adoption
  94. 150.

    OBLIGATORY SUMMARY SLIDE > Kotlin/Native can be a double-edged sword

    > You can move way faster on some things > Be mindful of the trade-offs you're making > Prioritize developer experience to drive adoption > Remember why you're using K/N in the first place
  95. 151.

    LINKS! > The (not so) hidden cost of sharing code

    between iOS and Android https://blogs.dropbox.com/tech/2019/08/ the-not-so-hidden-cost-of-sharing-code- between-ios-and-android/ > React Native at AirBnB https://medium.com/airbnb-engineering/ react-native-at-airbnb-f95aa460be1c