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. 2.
  2. 5.

    val reader = BufferedReader(...) reader.use { val line = it.readLine()

    // ... } with(user) { println(name) } val oneToOne: Pair<Int, String> = 1 to "one"
  3. 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"
  4. 7.

    val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

    / (it * it) } sqrt(sum * 6.0) println(pi) println(pi) lazy } {
  5. 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
  6. 9.

    val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

    / (it * it) } sqrt(sum * 6.0) println(pi) println(pi) LazyDelegate( }) {
  7. 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_!! } }
  8. 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_!! } }
  9. 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_!! } }
  10. 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_!! } }
  11. 14.

    val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

    1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi)
  12. 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) { }
  13. 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 { }
  14. 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
  15. 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!!
  16. 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!! } }
  17. 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!! } }
  18. 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!! } }
  19. 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!! } }
  20. 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!! } }
  21. 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!! } }
  22. 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
  23. 28.

    krate interface Krate { val sharedPreferences: SharedPreferences } abstract class

    SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) }
  24. 29.

    krate interface Krate { val sharedPreferences: SharedPreferences } abstract class

    SimpleKrate(context: Context) : Krate { override val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) } app
  25. 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) }
  26. 33.

    krate app interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { }
  27. 34.

    krate app interface Krate { val sharedPreferences: SharedPreferences } class

    MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? }
  28. 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
  29. 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 ) { }
  30. 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 ) { }
  31. 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
  32. 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
  33. 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
  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
  35. 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) }
  36. 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) }
  37. 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) }
  38. 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) }
  39. 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) }
  40. 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) }
  41. 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) }
  42. 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) }
  43. 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) }
  44. 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) }
  45. 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) }
  46. 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) }
  47. 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) }
  48. 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) }
  49. 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) }
  50. 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) }
  51. 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) }
  52. 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) }
  53. 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) }
  54. 61.
  55. 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)
  56. 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
  57. 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
  58. 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) } }
  59. 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) }
  60. 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) }
  61. 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) }
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. 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 }
  70. 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
  71. 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) }
  72. 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" ) ) )
  73. 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)
  74. 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)
  75. 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) )
  76. 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") } )
  77. 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)
  78. 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 {
  79. 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/