6

I'm trying to get myself familiar with DataStore, so in my current project, I'm trying to use it.

In my dependency. I've added :

    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06"

Then I created this class to handle data-store:

class BasicDataStore(context: Context) :
    PrefsDataStore(
        context,
        PREF_FILE_BASIC
    ),
    BasicImpl {
    override val serviceRunning: Flow<Boolean>
        get() = dataStore.data.map { preferences ->
            preferences[SERVICE_RUNNING_KEY] ?: false
        }
    override suspend fun setServiceRunningToStore(serviceRunning: Boolean) {
        dataStore.edit { preferences ->
            preferences[SERVICE_RUNNING_KEY] = serviceRunning
        }
    }
    companion object {
        private const val PREF_FILE_BASIC = "basic_preference"
        private val SERVICE_RUNNING_KEY = booleanPreferencesKey("service_running")
    }
}
@Singleton
interface BasicImpl {
    val serviceRunning: Flow<Boolean>
    suspend fun setServiceRunningToStore(serviceRunning: Boolean)
}

In my viewmodel, I'm trying to use that value, like this :

class MainViewModel(application: Application) : AndroidViewModel(application) {
    ...
    private val basicDataStore = BasicDataStore(application)
    val serviceRunning
            : StateFlow<Boolean> get()
            = basicDataStore.serviceRunning as StateFlow<Boolean>
    fun setServiceRunning(serviceRunning: Boolean) {
        viewModelScope.launch(IO) {
            basicDataStore.setServiceRunningToStore(serviceRunning)
        }
    }
}

But it gives me the following error :

Caused by: java.lang.ClassCastException: com.mua.roti.data.datastore.BasicDataStore$serviceRunning$$inlined$map$1 cannot be cast to kotlinx.coroutines.flow.StateFlow
        at com.mua.roti.viewmodel.MainViewModel.getServiceRunning(MainViewModel.kt:33)
...

In xml, in UI part :


        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{main.serviceRunning.value.toString()}" />

With viewmodel everything was so cool and easy, easy to read and implement. Now I'm confused with Flow. Thanks.

beigirad
  • 4,986
  • 2
  • 29
  • 52
Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
  • `StateFlow` is child of `Flow` So make sure `serviceRunning` returning `StateFlow` . – ADM Mar 24 '21 at 11:05
  • 1
    You can't cast it. so check the solution to convert it https://stackoverflow.com/a/66193573/2389923 – beigirad Mar 24 '21 at 11:32

1 Answers1

19

The hierarchy of flows is as follows: StateFlow -> SharedFlow -> Flow

So you can't really cast it, instead you should use the stateIn() operator if you'd like to convert your cold flow into a hot StateFlow. In your case:

val serviceRunning: StateFlow<Boolean> 
    get() = basicDataStore.serviceRunning.stateIn(viewModelScope, SharingStarted.Lazily, false)

You might tweak around the SharingStarted and/or initial value of the stateflow

Róbert Nagy
  • 6,720
  • 26
  • 45