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

What's new with Kotlin Multiplatform libraries?

What's new with Kotlin Multiplatform libraries?

How do I create libraries for Kotlin Multiplatform projects? Kotlin lets you share common code between different target platforms, but by default only a limit set of platform-agnostic APIs are available to the code. As Multiplatform development really starts to take off, there must also be a robust ecosystem of third party libraries available to application developers.

I’ll talk through what it looks like to create such a library, drawing from my experience building and maintaining one of my own starting in the early days of Kotlin/Native. We'll talk about how to find shared abstractions around different platform APIs, how to handle the fast-paced evolution of this environment, and what this all felt like as a first-time library developer. When we're done, you’ll be ready to leverage the growing ecosystem as well as make your own contributions.

Russell Wolf

October 08, 2020
Tweet

More Decks by Russell Wolf

Other Decks in Technology

Transcript

  1. What's new with Kotlin Multiplatform libraries? Russell Wolf Oct 8,

    2020 @Russhwolf ( or ) #session-chat-d1-s2-t3
  2. Kotlin • Kotlin/JVM on Android • Interop with Java •

    Preferred language for Android development
  3. Multiplatform Kotlin • Compile common code to multiple targets •

    JVM, Android, JS, iOS, Desktop, Embedded • Use platform-specific code to access platform APIs • Now in Alpha!
  4. Kotlin Mobile Multiplatform • Android (JVM) / iOS (Native) •

    Mobile is the killer app for Multiplatform • Same use-case • Similar capabilities
  5. Common Code // Common
 class Thing(val data: List<String>) { fun

    doSomething() { data.forEach { println(it) } } }
  6. Platform-Specific Code // Common
 expect val platform: String
 // Android


    actual val platform = "Android" 
 // iOS
 actual val platform = "iOS"
  7. Platform-Specific Code // Common
 interface Logger { fun log(message: String)

    }
 // Android
 class AndroidLogger : Logger { ... }
 // iOS - Kotlin
 class IosLogger { ... }
 // iOS - Swift class SwiftLogger : Logger { ... } // Common
 class TestLogger : Logger
  8. • Key-value storage based on platform APIs • Operators and

    Property Delegates • https://github.com/russhwolf/ multiplatform-settings Multiplatform Settings
  9. Multiplatform Settings // Common interface Settings { fun putInt(key: String,

    value: Int) }
 
 // Android class AndroidSettings( val delegate: SharedPreferences ) : Settings { override fun putInt(key: String, value: Int) = delegate.edit().putInt(key, value).apply() }

  10. Multiplatform Settings // Common interface Settings { fun putInt(key: String,

    value: Int) }
 // iOS class AppleSettings( val delegate: NSUserDefaults ) : Settings { override fun putInt(key: String, value: Int) = delegate.setInteger(value, key) }

  11. Multiplatform Settings // Common interface Settings { fun putInt(key: String,

    value: Int) }
 
 // JS class JsSettings( val delegate: Storage = localStorage ) : Settings { override fun putInt(key: String, value: Int) = delegate[key] = value.toString() }

  12. Multiplatform Settings // Common interface Settings { fun putInt(key: String,

    value: Int) }
 
 // JVM class JvmPreferencesSettings( val delegate: Preferences ) : Settings { override fun putInt(key: String, value: Int) = delegate.putInt(key, value) }

  13. Multiplatform Settings // Common interface Settings { fun putInt(key: String,

    value: Int) }
 
 // Windows class WindowsSettings( val rootKeyName: String ) : Settings { override fun putInt(key: String, value: Int) = putRegistryValue(key, REG_DWORD) { 
 alloc<ULONGVar> { this.value = value.toUInt() } 
 } }
  14. Multiplatform Settings // Common interface Settings { fun putInt(key: String,

    value: Int) }
 
 // Common (Tests) class MockSettings( val delegate: MutableMap<String, Any> ) : Settings { override fun putInt(key: String, value: Int) = delegate[key] = value }

  15. Multiplatform Settings // Common
 operator fun Settings.set(
 key: String, 


    value: Int
 ) = putInt(key, value) settings["a"] = 3 fun Settings.int(
 key: String? = null, 
 defaultValue: Int = 0
 ): ReadWriteProperty<Any?, Int> = ... var a by settings.int("a")
  16. Expect/Actual vs Interface expect class Settings { fun putInt(key: String,

    value: Int) } actual class Settings( val delegate: SharedPreferences ) { actual fun putInt(…) = delegate.putInt(…) } actual class Settings( val delegate: NSUserDefaults ) { actual fun putInt(…) = delegate.setInteger(…) }
  17. Expect/Actual vs Interface interface Settings { fun putInt(key: String, value:

    Int) } expect class PlatformSettings: Settings { override fun putInt(key: String, value: Int) } actual class PlatformSettings( val delegate: SharedPreferences ) : Settings { actual fun putInt(…) = delegate.putInt(…) } actual class PlatformSettings(…) { … }
  18. Expect/Actual vs Interface interface Settings { fun putInt(key: String, value:

    Int) } class AndroidSettings( val delegate: SharedPreferences ) : Settings { override fun putInt(…) = delegate.putInt(…) } class AppleSettings( val delegate: NSUserDefaults ) : Settings { override fun putInt(…) = delegate.setInteger(…) }
  19. Listener APIs • SharedPreferences .OnSharedPreferenceChangeListener • Passes key to callback

    • Might get called for repeated values • NSNotificationCenter NSUserDefaultsDidChangeNotification • Can't tell what changed
  20. Listener APIs val current = delegate.all[key] if (prev != current)

    { callback.invoke() prev = current } val current = delegate.objectForKey(key) if (prev != current) { callback.invoke() prev = current }
  21. Listener APIs val current = delegate.all[key] if (prev != current)

    { callback.invoke() prev = current } val current = delegate.objectForKey(key) if (prev != current) { callback.invoke() prev = current } ???
  22. Listener APIs val current = delegate.all[key] if (prev != current)

    { callback.invoke() prev = current } val current = delegate.objectForKey(key) if (prev != current) { callback.invoke() prev = current } ???
  23. Listener APIs interface ObservableSettings : Settings { … } class

    AndroidSettings(…) : ObservableSettings { … } class JsSettings(…) : Settings { … }
  24. Nullable Getters fun getInt(
 key: String,
 defaultValue: Int = 0


    ): Int
 
 fun getIntOrNull(
 key: String
 ): Int?
  25. Doc updates • Updated multiplatform docs page
 https://kotlinlang.org/docs/reference/mpp-intro.html • 1.4

    Migration docs
 https://kotlinlang.org/docs/reference/migrating- multiplatform-project-to-14.html • KMM Portal
 https://kotlinlang.org/lp/mobile/
  26. What about other stuff? • Jetbrains • Coroutines • Serialization

    • Ktor Client • Datetime • IO • AtomicFU
 
 
 • Community • https://github.com/ AAkira/Kotlin- Multiplatform-Libraries • https://kotlinlang.org/ lp/mobile/ecosystem/
  27. Thanks! • Questions? • @RussHWolf ( or ) • Multiplatform

    Settings
 https://github.com/russhwolf/multiplatform-settings
 • Kotlin Mobile Multiplatform
 https://kotlinlang.org/lp/mobile/ • Other community libraries
 https://github.com/AAkira/Kotlin-Multiplatform-Libraries • Kotlin 1.4 online event Oct 12-15
 https://kotlinlang.org/lp/event-14/