Delightful Delegate Design (Kotlin Budapest User Group meetup - April)

Delightful Delegate Design (Kotlin Budapest User Group meetup - April)

When developing a library, designing an easy to use API while hiding unnecessary implementation details from clients is fundamental. This talk and article looks at some of the API design choices we’ve made for our library Krate, an Android SharedPreferences wrapper.

Talk recording: https://www.youtube.com/watch?v=jTmHNo48zs0

4047c64e3a1e2f81addd4ba675ddc451?s=128

Marton Braun

April 11, 2019
Tweet

Transcript

  1. Delightful Delegate Design Braun Márton Szabolcs zsmb.co zsmb13 braun.marton@autsoft.hu

  2. None
  3. val oneToOne: Pair<Int, String> = 1 to "one"

  4. with(user) { println(name) } val oneToOne: Pair<Int, String> = 1

    to "one"
  5. val reader = BufferedReader(...) reader.use { val line = it.readLine()

    // ... } with(user) { println(name) } val oneToOne: Pair<Int, String> = 1 to "one"
  6. val pi: Double by lazy { val sum = (1..50_000).sumByDouble

    { 1.0 / (it * it) } sqrt(sum * 6.0) } println(pi) println(pi) val reader = BufferedReader(...) reader.use { val line = it.readLine() // ... } with(user) { println(name) } val oneToOne: Pair<Int, String> = 1 to "one"
  7. val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

    / (it * it) } sqrt(sum * 6.0) println(pi) println(pi) lazy } {
  8. val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

    / (it * it) } sqrt(sum * 6.0) println(pi) println(pi) lazy } { https://forums.swift.org/t/pitch-property-delegates/21895
  9. val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

    / (it * it) } sqrt(sum * 6.0) println(pi) println(pi) LazyDelegate( }) {
  10. class Constants { private var pi_: Double? = null val

    pi: Double get() { if (pi_ == null) { val sum = (1..50_000).sumByDouble { 1.0 / (it * it) } pi_ = sqrt(sum * 6.0) } return pi_!! } private var e_: Double? = null val e: Double get() { if (e_ == null) { val sum = (0..20).sumByDouble { 1.0 / (1..it).fold(1, { a, x -> a * x }) } e_ = sum } return e_!! } }
  11. class Constants { private var pi_: Double? = null val

    pi: Double get() { if (pi_ == null) { val sum = (1..50_000).sumByDouble { 1.0 / (it * it) } pi_ = sqrt(sum * 6.0) } return pi_!! } private var e_: Double? = null val e: Double get() { if (e_ == null) { val sum = (0..20).sumByDouble { 1.0 / (1..it).fold(1, { a, x -> a * x }) } e_ = sum } return e_!! } }
  12. class Constants { private var pi_: Double? = null val

    pi: Double get() { if (pi_ == null) { val sum = (1..50_000).sumByDouble { 1.0 / (it * it) } pi_ = sqrt(sum * 6.0) } return pi_!! } private var e_: Double? = null val e: Double get() { if (e_ == null) { val sum = (0..20).sumByDouble { 1.0 / (1..it).fold(1, { a, x -> a * x }) } e_ = sum } return e_!! } }
  13. class Constants { private var pi_: Double? = null val

    pi: Double get() { if (pi_ == null) { val sum = (1..50_000).sumByDouble { 1.0 / (it * it) } pi_ = sqrt(sum * 6.0) } return pi_!! } private var e_: Double? = null val e: Double get() { if (e_ == null) { val sum = (0..20).sumByDouble { 1.0 / (1..it).fold(1, { a, x -> a * x }) } e_ = sum } return e_!! } }
  14. val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi)
  15. val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi) class LazyDelegate<T : Any>(private val initializer: () -> T) { }
  16. val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi) class LazyDelegate<T : Any>(private val initializer: () -> T) { } operator fun getValue(thisRef: Any?, property: KProperty<*>): T { }
  17. val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi) class LazyDelegate<T : Any>(private val initializer: () -> T) { } operator fun getValue(thisRef: Any?, property: KProperty<*>): T { } private var value: T? = null
  18. class LazyDelegate<T : Any>(private val initializer: () -> T) {

    } val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble { 1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi) operator fun getValue(thisRef: Any?, property: KProperty<*>): T { } private var value: T? = null if (value == null) { value = initializer() } return value!!
  19. val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) // 12597400 ns println(pi) // 72100 ns class LazyDelegate<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } }
  20. val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) // 12597400 ns println(pi) // 72100 ns class LazyDelegate<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } }
  21. val pi: Double by LazyDelegate { val sum = (1..50_000).sumByDouble

    { 1.0 / (it * it) } sqrt(sum * 6.0) } println(pi) // 12597400 ns println(pi) // 72100 ns class LazyDelegate<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } }
  22. val pi: Double by lazy { val sum = (1..50_000).sumByDouble

    { 1.0 / (it * it) } sqrt(sum * 6.0) } println(pi) // 12597400 ns println(pi) // 72100 ns class LazyDelegate<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } }
  23. val pi: Double by lazy { val sum = (1..50_000).sumByDouble

    { 1.0 / (it * it) } sqrt(sum * 6.0) } println(pi) // 12597400 ns println(pi) // 72100 ns class lazy<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } }
  24. val pi: Double by lazy { val sum = (1..50_000).sumByDouble

    { 1.0 / (it * it) } sqrt(sum * 6.0) } println(pi) // 12597400 ns println(pi) // 72100 ns class LazyDelegate<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } }
  25. fun <T : Any> lazy(initializer: () -> T) = LazyDelegate(initializer)

    class LazyDelegate<T : Any>(private val initializer: () -> T) { private var value: T? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (value == null) { value = initializer() } return value!! } } val pi: Double by lazy { val sum = (1..50_000).sumByDouble { 1.0 / (it * it) } sqrt(sum * 6.0) } println(pi) // 12597400 ns println(pi) // 72100 ns
  26. https://github.com/AutSoft/Krate

  27. krate interface Krate { val sharedPreferences: SharedPreferences }

  28. krate interface Krate { val sharedPreferences: SharedPreferences } abstract class

    SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  29. krate interface Krate { val sharedPreferences: SharedPreferences } abstract class

    SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) } app
  30. app krate class MyKrate(context: Context) : SimpleKrate(context) { var onboarded

    by booleanPref("onboarded") var appOpenCount by intPref("appOpenCount") var username by stringPref("username") } myKrate.onboarded = true myKrate.appOpenCount++ myKrate.username = "t1gg3r" interface Krate { val sharedPreferences: SharedPreferences } abstract class SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  31. app krate interface Krate { val sharedPreferences: SharedPreferences }

  32. krate app interface Krate { val sharedPreferences: SharedPreferences }

  33. krate app interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { }
  34. krate app interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? }
  35. app krate interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34
  36. app krate interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34 class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { }
  37. krate app interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34 class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { }
  38. app krate interface Krate { val sharedPreferences: SharedPreferences } class

    IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { } } class MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34
  39. app krate interface Krate { val sharedPreferences: SharedPreferences } class

    IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } class MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34
  40. app krate interface Krate { val sharedPreferences: SharedPreferences } class

    IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } class MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34
  41. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by IntDelegate(sharedPreferences, "score") } krate interface Krate { val sharedPreferences: SharedPreferences } class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } } println(myKrate.score) // 0 myKrate.score = 34 println(myKrate.score) // 34
  42. app krate interface Krate { val sharedPreferences: SharedPreferences } class

    IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } class MyKrate(context: Context) : SimpleKrate(context) { var score: Int by IntDelegate(sharedPreferences, "score") } fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  43. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate interface Krate { val sharedPreferences: SharedPreferences } class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  44. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate interface Krate { val sharedPreferences: SharedPreferences } class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  45. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate interface Krate { val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  46. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  47. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  48. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  49. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  50. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  51. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  52. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  53. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  54. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  55. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  56. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  57. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  58. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return thisRef.sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { thisRef.sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(sharedPreferences, key) }
  59. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return thisRef.sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { thisRef.sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): IntDelegate { return IntDelegate(key) }
  60. app class MyKrate(context: Context) : SimpleKrate(context) { var score: Int

    by intPref("score") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class IntDelegate( private val key: String ) : ReadWriteProperty<Krate, Int> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Int { return thisRef.sharedPreferences.getInt(key, 0) } override operator fun setValue(thisRef: Krate, property: KProperty<*>, value: Int) { thisRef.sharedPreferences.edit().putInt(key, value).apply() } } public fun Krate.intPref(key: String): ReadWriteProperty<Krate, Int> { return IntDelegate(key) }
  61. app krate

  62. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { var users: List<User>? by ??? } myKrate.users = listOf(User("William"), User("Virginia")) println(myKrate.users)
  63. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { va users: List<User>? by gsonPref("users") } myKrate.users = listOf(User("William"), User("Virginia")) println(myKrate.users) r
  64. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { va users: List<User>? by gsonPref("users") } myKrate.users = listOf(User("William"), User("Virginia")) println(myKrate.users) l
  65. app myKrate.users = listOf(User("William"), User("Virginia")) println(myKrate.users) class MyKrate(context: Context) :

    SimpleKrate(context) { val users: List<User>? by gsonPref("users") } krate public interface Krate { public val sharedPreferences: SharedPreferences } internal class GsonDelegate<T : Any>( private val key: String ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, object : TypeToken<T>() {}.type) } }
  66. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by gsonPref("users") } internal class GsonDelegate<T : Any>( private val key: String ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, object : TypeToken<T>() {}.type) } } public fun <T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key) }
  67. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by gsonPref("users") } internal class GsonDelegate<Object>( private val key: String ) : ReadOnlyProperty<Krate, Object?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): Object? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, object : TypeToken<Object>() {}.type) } } public fun <Object> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, Object?> { return GsonDelegate(key) }
  68. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by gsonPref("users") } internal class GsonDelegate<T : Any>( private val key: String ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, object : TypeToken<T>() {}.type) } } public fun <T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key) }
  69. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by gsonPref("users") } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key) } object : TypeToken<T>() {}.type) ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } internal class GsonDelegate<T : Any>( private val key: String
  70. app gsonPref("users") class MyKrate(context: Context) : SimpleKrate(context) { val users:

    List<User>? by } krate public interface Krate { public val sharedPreferences: SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } object : TypeToken<T>() {}.type) ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } internal class GsonDelegate<T : Any>( private val key: String
  71. app class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>?

    by } gsonPref("users") krate public interface Krate { public val sharedPreferences: SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String
  72. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } GsonDelegate(key, object : TypeToken<T>() {}.type) class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by } gsonPref("users") ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String
  73. app class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>?

    by } GsonDelegate( , object : TypeToken< >() {}.type) "users" gsonPref("users") krate public interface Krate { public val sharedPreferences: SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String T
  74. app GsonDelegate( , object : TypeToken< >() {}.type) "users" List<User>

    gsonPref("users") krate public interface Krate { public val sharedPreferences: SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by } ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String
  75. app gsonPref("users") class MyKrate(context: Context) : SimpleKrate(context) { val users:

    List<User>? by } krate public interface Krate { public val sharedPreferences: SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String
  76. app gsonPref("users") krate public interface Krate { public val sharedPreferences:

    SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by }
  77. app class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>?

    by } gsonPref("users") GsonDelegate(...) krate public interface Krate { public val sharedPreferences: SharedPreferences } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) } ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, } } , private val type: Type type) internal class GsonDelegate<T : Any>( private val key: String
  78. app krate public interface Krate { public val sharedPreferences: SharedPreferences

    } class MyKrate(context: Context) : SimpleKrate(context) { val users: List<User>? by gsonPref("users") } @PublishedApi internal class GsonDelegate<T : Any>( private val key: String, private val type: Type ) : ReadOnlyProperty<Krate, T?> { override operator fun getValue(thisRef: Krate, property: KProperty<*>): T? { val string = thisRef.sharedPreferences.getString(key, null) return Gson().fromJson(string, type) } } public inline fun <reified T : Any> Krate.gsonPref( key: String ): ReadOnlyProperty<Krate, T?> { return GsonDelegate(key, object : TypeToken<T>() {}.type) }
  79. https://zsmb.co/maintaining-compatibility-in-kotlin-libraries/

  80. Krate* class MyKrate(context: Context) : SimpleKrate(context) { var onboarded var

    appOpenCount var username } myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username) by booleanPref("onboarded" by intPref("appOpenCount" by stringPref("username" ) ) )
  81. class MyKrate(context: Context) : SimpleKrate(context) { var onboarded: Boolean by

    booleanPref("onboarded", defaultValue = false) var appOpenCount: Int by intPref("appOpenCount", defaultValue = 0) var username: String? by stringPref("username") } Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username)
  82. class MyKrate(context: Context) : SimpleKrate(context) { // non-nullable, has default

    value var onboarded: Boolean by booleanPref("onboarded", defaultValue = false) var appOpenCount: Int by intPref("appOpenCount", defaultValue = 0) // nullable, null by default var username: String? by stringPref("username") } Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username)
  83. class MyKrate(context: Context) : SimpleKrate(context { // non-nullable, has default

    value var onboarded: Boolean by booleanPref("onboarded", defaultValue = false) var appOpenCount: Int by intPref("appOpenCount", defaultValue = 0) // nullable, null by default var username: String? by stringPref("username") } Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username) )
  84. Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username) class MyKrate(context: Context) :

    SimpleKrate(context , AppSettings { // non-nullable, has default value override var onboarded: Boolean by booleanPref("onboarded", defaultValue = false) override var appOpenCount: Int by intPref("appOpenCount", defaultValue = 0) // nullable, null by default override var username: String? by stringPref("username") } )
  85. class MyKrate @Inject constructor(context: Context) // non-nullable, has default value

    override var onboarded: Boolean by booleanPref("onboarded", defaultValue = false) override var appOpenCount: Int by intPref("appOpenCount", defaultValue = 0) // nullable, null by default override var username: String? by stringPref("username") } : SimpleKrate(context), AppSetti Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username)
  86. class MyKrate @Inject constructor(context: Context) // non-nullable, has default value

    override var onboarded: Boolean by booleanPref("onboarded", defaultValue = false) override var appOpenCount: Int by intPref("appOpenCount", defaultValue = 0) // nullable, null by default override var username: String? by stringPref("username") } Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username) : SimpleKrate(context), AppSettings {
  87. References • Krate  https://github.com/AutSoft/Krate • Delightful Delegate Design 

    https://blog.autsoft.hu/delightful-delegate-design/ • Further reading  Maintaining Compatibility in Kotlin libraries  https://zsmb.co/maintaining-compatibility-in-kotlin-libraries/  Swift Pitch: Property Delegates  https://forums.swift.org/t/pitch-property-delegates/21895  DSL Design  https://zsmb.co/kotlin-dsl-design-with-village-dsl/  Tips for writing a library  https://www.kotlindevelopment.com/tips-for-writing-a-library-in-kotlin/
  88. Questions? zsmb.co zsmb13 braun.marton@autsoft.hu Photo by Agnieszka Kowalczyk on Unsplash