I have a SharedFlow in ViewModel, where I make a call to repository, to retrieve from the database a single record by some id. This is will be set whenever user will click on some record in RecyclerView. The problem is I constantly getting null, but if I hardcode id in repository parameter, then everything works fine.
DAO
@Query("SELECT * FROM employees WHERE id = :id")
fun getEmployeeById(id: Int?): Flow<EmployeeModel>
RepositoryInterface
fun getEmployeeById(id: Int?): Flow<EmployeeModel>
RepositoryImplementation
override fun getEmployeeById(id: Int?): Flow<EmployeeModel> {
return employeeDAO.getEmployeeById(id)
}
ViewModel
var employeeById: SharedFlow<DatabaseState<EmployeeModel>> = repository.get().getEmployeeById(employeeId.value?.toInt())
.map {
println("onCreateView in VM. ID ${employeeId.value} | data: $it")
DatabaseState.Success(it) }
.catch { DatabaseState.Error(it.message) }
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)
Fragment
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mViewModel.employeeById.collect{ employee ->
when (employee){
is DatabaseState.Success -> {
Log.i(TAG, "onCreateView APDEJCIK: ${mViewModel.employeeId.value} | ${employee.data}")
}
is DatabaseState.Error -> {
Log.i(TAG, "onCreateView: Failed to retrieve data about employee in UpdateFragmentEmployee fragment")}
}
}
}
}
As you can see, I'm loging the ID from the ViewModel a couple of times, and it every times has correct id to the position I've clicked, so the ID should be ok.
Edit: Model class
@Entity(tableName = "employees")
data class EmployeeModel(
@PrimaryKey(autoGenerate = true)
val id: Int,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "surname")
val surname: String,
@ColumnInfo(name = "age")
val age: Int,
@ColumnInfo(name = "workplace")
val workplace: String,
@ColumnInfo(name = "salary")
val salary: Double
)
I think these code below have a problem
var employeeById: SharedFlow<DatabaseState<EmployeeModel>> = repository.get().getEmployeeById(employeeId.value?.toInt())
.map {
println("onCreateView in VM. ID ${employeeId.value} | data: $it")
DatabaseState.Success(it) }
.catch { DatabaseState.Error(it.message) }
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)
Because of this declaration, your employeeById
will be created when your view model is created, so employeeId.value
is still null.
Then, because of SharingStarted.WhileSubscribed()
. the map function will only get called when your flow has a subscriber on your repeatOnLifecycle(Lifecycle.State.STARTED)
. At this time, your employeeId.value
is set the correct value. This is why you get a very weird log.
To fix your issue, I think some things need to be changed.
Your DAO
@Query("SELECT * FROM employees WHERE id = :id")
fun getEmployeeById(id: Int): Flow<EmployeeModel?>
Your viewModel. I assume that you have a state flow for your employee. You should use flatMap
to update your employeeById value whenever your employee changes.
val employeeById: SharedFlow<DatabaseState<EmployeeModel>> = employeeId.filterNotNull().flatMapLatest {
repository.get().getEmployeeById(it.toInt())
}.map {
if (it!= null) DatabaseState.Success(it) else DatabaseState.Error("NOT FOUND")
}.catch { DatabaseState.Error(it.message) }
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)
Last thing, if you use employeeById
to display data. Consider using StateFlow
instead of SharedFlow