Avoid default values in Androids SharedPreferences

Viewed 198

SharedPreferences provide the following function to retrieve a string:

String getString(String key, @Nullable String defValue);

When storing an Int, the default-value is not-nullable:

int getInt(String key, int defValue);

I now am looking to store and retrieve a nullable Int?, e.g.:

var timerDuration: Int?
    get() = prefs.getIntOrNull(TIMER_DURATION)
    set(value) { prefs.edit { putIntOrNull(TIMER_DURATION, value) } }

Solution 1: when value is null, remove key

private fun SharedPreferences.getIntOrNull(key: String) =
    if (prefs.contains(key)) {
        getInt(key, 12345)
    } else {
        null
    }

private fun SharedPreferences.Editor.putIntOrNull(key: String, value: Int?) =
    if (value == null) {
        remove(key)
    } else {
        putInt(key, value)
    }

... but i'm not sure if i could get problems with multiple, quick accesses because of the asynchronous nature of shared preferences?


Solution 2: use the string-option for everything:

private fun SharedPreferences.getIntOrNull(key: String) =
    prefs.getString(key, null)?.toIntOrNull()

private fun SharedPreferences.Editor.putIntOrNull(key: String, value: Int?) =
    putString(key, value?.toString())

... but .toIntOrNull() feels like a lot of overhead for such a simple task?


Why would i like to do this?

I hope Kotlin Multiplattform allows me to add an iOS-Version to my existing Android-App. My goal is a "core" module written completely in Kotlin with no plattform-specific dependencies. The "core" then just uses this interface, which is implemented by the Android & iOS - Apps

interface SettingsStorage {

    var timerDuration: Int?

    [...]
}

And as i would not like to have duplicate default-value-logic i'd enjoy to handle that in my "core"-module


Are there any other (better) options? I'm feeling like i'm reinventing the wheel...

2 Answers

Either of your methods is fine. SharedPreferences is thread-safe. Option 1 could only really fail if you set up a service in your manifest to run in a separate process and it is modifying the preferences at the same time as the rest of your app. This isn't something you would do accidentally.

Your second method isn't any additional overhead, because under the hood, SharedPreferences are all Strings so your code is basically doing the same thing as SharedPreferences.getInt, except that returns the default value when the preference doesn't exist or cant be parsed as an Int.

When you store an Int to SharedPreferences, it is stored internally as a string. So for getting, just get a String and if it's a null returns it, or try parse it to an Int. For setting, just convert your Int to String and put it using putString method.

var timerDuration: Int?
    get() = try { prefs.getString(TIMER_DURATION, null)?.toInt() } catch (e: Exception) { null }
    set(value) { prefs.edit { putString(TIMER_DURATION, value.toString()) } }
Related