Márton Braun
April 11, 2019
350

# 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.

April 11, 2019

## Transcript

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

to "one"

// ... } with(user) { println(name) } val oneToOne: Pair<Int, String> = 1 to "one"
5. ### 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"
6. ### val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

/ (it * it) } sqrt(sum * 6.0) println(pi) println(pi) lazy } {
7. ### 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
8. ### val pi: Double by val sum = (1..50_000).sumByDouble { 1.0

/ (it * it) } sqrt(sum * 6.0) println(pi) println(pi) LazyDelegate( }) {
9. ### 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. ### 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. ### val pi: Double by LazyDelegate({ val sum = (1..50_000).sumByDouble {

1.0 / (it * it) } sqrt(sum * 6.0) }) println(pi) println(pi)
14. ### 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) { }
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) { } operator fun getValue(thisRef: Any?, property: KProperty<*>): 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 { } private var value: T? = null
17. ### 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!!
18. ### 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. ### 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 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. ### 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!! } }
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 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!! } }
24. ### 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

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

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

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

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

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

MyKrate(context: Context) : SimpleKrate(context) { var score: Int by ??? }
34. ### 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
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 class IntDelegate( private val sharedPreferences: SharedPreferences, private val key: String ) { }
36. ### 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 ) { }
37. ### 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
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 { 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
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 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
41. ### 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) }
42. ### 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) }
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 } 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) }
45. ### 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) }
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 ) : 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) }
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> { 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) }
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: 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) }
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 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) }
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 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) }
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): ReadWriteProperty<Krate, Int> { return IntDelegate(key) }

61. ### 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)
62. ### 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
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) l
64. ### 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) } }
65. ### 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) }
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<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) }
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<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) }
68. ### 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
69. ### 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
70. ### 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
71. ### 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
72. ### 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
73. ### 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
74. ### 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
75. ### 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 }
76. ### 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
77. ### 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. ### 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" ) ) )
80. ### 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)
81. ### 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)
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. ### 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") } )
84. ### 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)
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") } Krate* myKrate.onboarded = true myKrate.appOpenCount++ println(myKrate.username) : SimpleKrate(context), AppSettings {
86. ### 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/