Поле расширения Kotlin для значения SharedPreference по ключу строковой константы

Функции расширения отлично подходят для API SharedPreference в Android. У Джейка Уортона есть интересная реализация во временном коде 32:30 этого видеоурока, где он реализует функцию расширения SharedPreferences следующим образом:

preferences.edit{
    set(USER_ID /*some string key constant somewhere*/, 42)
    //...
}

в то время как это нормально, его вид многословен.

В этом уроке от Krupal Shah объясняется, как вы можете уменьшить функции расширения методов получения / установки для SharedPreferences до:

preferences[USER_ID] = 42 
Log.i("User Id", preferences[USER_ID]) //User Id: 42    

Это довольно хорошо, но скобки подразумевают итеративную семантику, IMO. Хотя это и не самое худшее в мире, вы просто хотели бы реализовать расширение поля значения SharedPreferences самой константой ключа.

У меня вопрос, есть ли способ реализовать этот тип расширения на SharedPreferences?

preferences.USER_ID = 42
Log.i("User Id", preferences.USER_ID) //User Id: 42

2 ответа

Решение

Во-первых, давайте создадим общий интерфейс для предоставления экземпляра SharedPreferences:

interface SharedPreferencesProvider {

    val sharedPreferences: SharedPreferences
}

После того, как мы должны создать делегат для свойства, которое будет читать / записывать значение в настройки:

object PreferencesDelegates {

    fun string(
        defaultValue: String = "",
        key: String? = null
    ): ReadWriteProperty<SharedPreferencesProvider, String> = 
        StringPreferencesProperty(defaultValue, key)
}

private class StringPreferencesProperty(
    private val defaultValue: String,
    private val key: String?
) : ReadWriteProperty<SharedPreferencesProvider, String> {

    override fun getValue(
         thisRef: SharedPreferencesProvider, 
         property: KProperty<*>
    ): String {
        val key = key ?: property.name
        return thisRef.sharedPreferences.getString(key, defaultValue)
    }

    override fun setValue(
        thisRef: SharedPreferencesProvider,
        property: KProperty<*>, 
        value: String
    ) {
        val key = key ?: property.name
        thisRef.sharedPreferences.save(key, value)
    }
}

PreferencesDelegates Нужно было скрыть реализацию и добавить читабельность к коду. В конце концов это можно использовать так:

class AccountRepository(
    override val sharedPreferences: SharedPreferences
) : SharedPreferencesProvider {

    var currentUserId by PreferencesDelegates.string()
    var currentUserName by string() //With import
    var currentUserNickname by string(key = "CUSTOM_KEY", defaultValue = "Unknown")

    fun saveUser(id: String, name: String) {
        this.currentUserId = id
        this.currentUserName = name
    }
}

Подобное можно реализовать int, float или даже пользовательский тип:

open class CustomPreferencesProperty<T>(
    defaultValue: T,
    private val key: String?,
    private val getMapper: (String) -> T,
    private val setMapper: (T) -> String = { it.toString() }
) : ReadWriteProperty<SharedPreferencesProvider, T> {

    private val defaultValueRaw: String = setMapper(defaultValue)

    override fun getValue(
        thisRef: SharedPreferencesProvider, 
        property: KProperty<*>
    ): T {
        val key = property.name
        return getMapper(thisRef.sharedPreferences.getString(key, defaultValueRaw))
    }

    override fun setValue(
        thisRef: SharedPreferencesProvider, 
        property: KProperty<*>, 
        value: T
    ) {
        val key = property.name
        thisRef.sharedPreferences.save(key, setMapper(value))
    }
}

Я написал небольшую библиотеку, которая покрывает такой случай. Вы можете найти остальные реализованные настройки здесь

РЕДАКТИРОВАТЬ. В случае, если вы используете кинжал:

class AccountRepository @Injcet constructor() : SharedPreferencesProvider {

    @Inject
    override lateinit var sharedPreferences: SharedPreferences

    var currentUserId by PreferencesDelegates.string()
    ...

}

Вы можете определить простое свойство расширения с помощью getter и setter

var SharedPreferences.userId
    get() = getInt(USER_ID, 0)
    set(value: Int) { edit().putInt(USER_ID, value).apply() }
Другие вопросы по тегам