Marton Braun
April 11, 2019
250

# 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

April 11, 2019

## Transcript

1. Delightful Delegate Design
Braun Márton Szabolcs
zsmb.co zsmb13
[email protected]

2. val oneToOne: Pair = 1 to "one"

3. with(user) {
println(name)
}
val oneToOne: Pair = 1 to "one"

4. val reader = BufferedReader(...)
reader.use {
val line = it.readLine()
// ...
}
with(user) {
println(name)
}
val oneToOne: Pair = 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 = 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(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(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(private val initializer: () -> T) {
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
}
private var value: T? = null

17. class LazyDelegate(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(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(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(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(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(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(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 lazy(initializer: () -> T) = LazyDelegate(initializer)
class LazyDelegate(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

25. https://github.com/AutSoft/Krate

26. krate
interface Krate {
val sharedPreferences: SharedPreferences
}

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)
}

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

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

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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
return IntDelegate(key)
}

60. app
krate

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

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

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

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

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

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

70. app
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
}
gsonPref("users")
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
) : ReadOnlyProperty {
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(
private val key: String

71. app
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
GsonDelegate(key, object : TypeToken() {}.type)
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
} gsonPref("users")
) : ReadOnlyProperty {
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(
private val key: String

72. app
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
}
GsonDelegate( , object : TypeToken< >() {}.type)
"users"
gsonPref("users")
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
) : ReadOnlyProperty {
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(
private val key: String
T

73. app
GsonDelegate( , object : TypeToken< >() {}.type)
"users" List
gsonPref("users")
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
}
) : ReadOnlyProperty {
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(
private val key: String

74. app
gsonPref("users")
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
}
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
) : ReadOnlyProperty {
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(
private val key: String

75. app
gsonPref("users")
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
) : ReadOnlyProperty {
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(
private val key: String
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
}

76. app
class MyKrate(context: Context) : SimpleKrate(context) {
val users: List? by
} gsonPref("users")
GsonDelegate(...)
krate
public interface Krate {
public val sharedPreferences: SharedPreferences
}
public inline fun Krate.gsonPref(
key: String
): ReadOnlyProperty {
return GsonDelegate(key, object : TypeToken() {}.type)
}
) : ReadOnlyProperty {
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(
private val key: String

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

78. https://zsmb.co/maintaining-compatibility-in-kotlin-libraries/

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/

87. Questions?
zsmb.co zsmb13
[email protected]
Photo by Agnieszka Kowalczyk on Unsplash