Is there a way to set the minimum and maximum value of a DataStore Preferences value?

21 Views Asked by At

I am wondering whether it's possible to ensure that a value doesn't go below 0 and not above 5 in the userManager (or Datastore and equivalent) class. If not, then what should I do?

class userManager(context: Context) {

    private val datastore = context.datastore

    companion object {
        val NUMBER_OF_LIVES_KEY = intPreferencesKey("number_of_lives")
    }

    suspend fun setNumberOfLives(numberOfLives: Int){
        datastore.edit {
            it[NUMBER_OF_LIVES_KEY] = numberOfLives
        }
    }

    val getNumberOfLives: Flow<Int> = datastore.data.map {

        if(it[NUMBER_OF_LIVES_KEY]!! > 5){
            it[NUMBER_OF_LIVES_KEY] == 5
        }

        else if(it[NUMBER_OF_LIVES_KEY]!! < 0){
            it[NUMBER_OF_LIVES_KEY] == 0
        }

        it[NUMBER_OF_LIVES_KEY] ?: 5
    }

}

I have attempted the if statements and it gave me a "java.lang.NullPointerException" error message.

1

There are 1 best solutions below

0
Leviathan On

https://kotlinlang.org/docs/null-safety.html#the-operator describes it pretty well what happens when you use !!:

[...] if you want an NPE, you can have it, but you have to ask for it explicitly and it won't appear out of the blue.

Where NPE stands for NullPointerException, the error that you experience.

The solution is easy: If you don't want your code to abort with such an exception, remove the !!. That way the compiler will prevent you from compiling the erroneous code in the first place and forces you to fix it before you run it.

The underlying problem is that the property you try to access does not exist. That is perfectly normal if the app was freshly installed or the user wiped all data. What you want to do instead is handle that situation gracefully by supplying a default value in that case. You actually do that already in your last line of code, but that's too late if you want to work with the value beforehand.

Now there is the other problem that your getter actually also saves data in some cases. Instead, I would suggest that you move everything that changes the preference value into the setter method:

suspend fun setNumberOfLives(numberOfLives: Int){
    datastore.edit {
        if (numberOfLives > 5) {
            it[NUMBER_OF_LIVES_KEY] == 5
        }

        else if (numberOfLives < 0) {
            it[NUMBER_OF_LIVES_KEY] == 0
        }

        else {
            it[NUMBER_OF_LIVES_KEY] = numberOfLives
        }
    }
}

That could be drastically simplified though:

suspend fun setNumberOfLives(numberOfLives: Int) {
    datastore.edit {
        it[NUMBER_OF_LIVES_KEY] = numberOfLives.coerceIn(0, 5)
    }
}

Now your getter only retrieves the preference value and handles the case when it doesn't exist:

val getNumberOfLives: Flow<Int> = datastore.data.map {
    it[NUMBER_OF_LIVES_KEY] ?: 5
}

This way the code is more concise and less error prone.