Today is a good time to talk about SharedPreferences. How Kotlin can helps make using it more easy?

The first thing that comes to mind is custom getter an setter:

internal val sharedPreferences: SharedPreferences = context.getSharedPreferences("MY_PREFS", Context.MODE_PRIVATE)

var spInt: Int
    get() = sharedPreferences.getInt("spInt", 0)
    set(value) = sharedPreferences.edit().putInt("spInt", value).apply()

This trick functional, but not so graceful. Let’s try to use delegates:

private operator fun SharedPreferences.setValue(spManager: SPManager, property: KProperty<*>, l: Long) {
    spManager.sharedPreferences.edit().putLong(property.name, l).apply()
}

private operator fun SharedPreferences.getValue(spManager: SPManager, property: KProperty<*>): Long {
    return spManager.sharedPreferences.getLong(property.name, 0)
}

Now we can declare variables using tis delegates:

var spLong: Long by sharedPreferences

But this flow has two dirty moments: we can’t change the default value, and SharedPreferences key will be same all the time. It’s time to level up:

fun SharedPreferences.string(name: String, defValue: String? = null) = StringPrefs(name, defValue, this)

class StringPrefs(private val name: String, private val defValue: String?, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>): String? = prefs.getString(name, defValue)
    operator fun setValue(thisRef: Any, property: KProperty<*>, s: String?) = prefs.edit().putString(name, s).apply()
}

Now we can declare variables more easy (defValue parameter is optional):

var spString: String? by sharedPreferences.string(name = "spString", defvalue = "none")

Last upgrade: we can move all delegates in separate file and use it in whole project:

fun SharedPreferences.boolean(name: String, defValue: Boolean = false) = BooleanPrefs(name, defValue, this)
fun SharedPreferences.float(name: String, defValue: Float = 0f) = FloatPrefs(name, defValue, this)
fun SharedPreferences.int(name: String, defValue: Int = 0) = IntPrefs(name, defValue, this)
fun SharedPreferences.long(name: String, defValue: Long = 0L) = LongPrefs(name, defValue, this)
fun SharedPreferences.string(name: String, defValue: String = "") = StringPrefs(name, defValue, this)
fun SharedPreferences.nullableString(name: String, defValue: String? = null) = NullableStringPrefs(name, defValue, this)

class BooleanPrefs(private val name: String, private val defValue: Boolean, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>) = prefs.getBoolean(name, defValue)
    operator fun setValue(thisRef: Any, property: KProperty<*>, b: Boolean) = prefs.edit().putBoolean(name, b).apply()
}

class FloatPrefs(private val name: String, private val defValue: Float, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>) = prefs.getFloat(name, defValue)
    operator fun setValue(thisRef: Any, property: KProperty<*>, f: Float) = prefs.edit().putFloat(name, f).apply()
}

class IntPrefs(private val name: String, private val defValue: Int, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>) = prefs.getInt(name, defValue)
    operator fun setValue(thisRef: Any, property: KProperty<*>, i: Int) = prefs.edit().putInt(name, i).apply()
}

class LongPrefs(private val name: String, private val defValue: Long, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>) = prefs.getLong(name, defValue)
    operator fun setValue(thisRef: Any, property: KProperty<*>, l: Long) = prefs.edit().putLong(name, l).apply()
}

class StringPrefs(private val name: String, private val defValue: String, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>): String = prefs.getString(name, defValue) ?: defValue
    operator fun setValue(thisRef: Any, property: KProperty<*>, s: String) = prefs.edit().putString(name, s).apply()
}

class NullableStringPrefs(private val name: String, private val defValue: String?, private val prefs: SharedPreferences) {
    operator fun getValue(thisRef: Any, property: KProperty<*>): String? = prefs.getString(name, defValue)
    operator fun setValue(thisRef: Any, property: KProperty<*>, s: String?) = prefs.edit().putString(name, s).apply()
}

How to use:

var spString1: String by sharedPreferences.string(name = "spString1")
var spString2: String by sharedPreferences.string(name = "spString2", defValue = "def")
var spString3: String? by sharedPreferences.nullableString(name = "spString3", defValue = "def")
var spString4: String? by sharedPreferences.nullableString(name = "spString4", defValue = null)
var spBoolean: Boolean by sharedPreferences.boolean(name = "spBoolean")
var spFloat: Float by sharedPreferences.float(name = "spFloat", defValue = 1f)

Leave a Reply