You could go one step further and wrap the property with a delegate, making use of the variable name on the meta data.
Usage
class SomeActivity : SomeBaseActivity {
// Declare property the with key "myImportantNumber"
// and default value 10
var myImportantNumber by preference(10)
//how to access the property
fun isMyImportantNumberReallyHight() = myImportantNumber > 100
//how to edit the property
fun incrementMyImportantNumber(times:Int){
myImportantNumber = myImportantNumber + times
}
}
Delegation
The delegate keeps an instance of some preferences manager and uses the meta data on the property to fetch and save values on the shared preference.
class DelegatedPreference<T>(val default: T, val contextProvider:()-> Context) {
val manager by lazy { PreferencesManager(contextProvider()) }
@Suppress("UNCHECKED_CAST")
operator fun getValue(thisRef: Any?, prop: KProperty<*>): T {
return manager.get(prop.name, default)
}
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: Any) {
manager.save(prop.name, value)
}
class TypeNotImplementedException(val propName:String) : Exception("Type of ${propName} is not implemented on DelegatedPreference and thus invalid")
}
Some sugar
A little extension method:
fun <T> Activity.preference(default:T):DelegatedPreference<T>{
return DelegatedPreference(default, {this})
}
Which allow us to change this:
var myImportantNumber by DelegatedPreference(10, {this})
By something more readable:
var myImportantNumber by preference(10)
The actual getting and saving
Here what I called PreferencesManager
(sorry I didn't come up with a better name) does the heavy lifting, and calls .edit()
each time a property needs to be altered. It would look something like:
public class PreferencesManager(context: Context) {
private val preferences = getSharedPreferences(context)
companion object Utils {
public val APP_PREFERENCES: String = "APP_PREFERENCES"
fun getSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE)
}
}
public fun save(label:String, elem:Any){
when(elem){
is Int -> preferences.edit().putInt(label, elem).apply()
is String -> preferences.edit().putString(label, elem).apply()
is Float -> preferences.edit().putFloat(label, elem).apply()
is Boolean -> preferences.edit().putBoolean(label, elem).apply()
else -> throw DelegatedPreference.TypeNotImplementedException(label)
}
}
@Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
public fun <T> get(label:String, default:T):T = when(default){
is Int -> preferences.getInt(label, default)
is String -> preferences.getString(label, default)
is Float -> preferences.getFloat(label, default)
is Boolean -> preferences.getBoolean(label, default)
else -> throw DelegatedPreference.TypeNotImplementedException(label)
} as T
}
There is a lot of room for improvement here (like parametrizing the preference name instead of hardcoding it, giving an extension point for serializing other types, etc), but the general idea remains.