I have a very strange issue. I am trying to run an API call in a ViewModel using a clean architecture-style repository object. I am able to access the repo object in the init block in a viewModelScope coroutine
I have a simple workaround; I simply don't call the function from the init block and instead expose a public function and call it from a fragment; so I have a solution but it is an interesting problem.
@HiltViewModel
class MainViewModel @Inject constructor(repo: JobsRepository) : ViewModel(){
@Inject lateinit var repo : JobsRepository
lateinit var jobDao: JobDao
val jobsListUpdate: MutableLiveData<Resource<JobsResponse>> = MutableLiveData()
var jobsListResponse: JobsResponse? = null
init {
jobDao = repo.getDao()
// getJobsList()
viewModelScope.launch {
jobsListUpdate.postValue(Resource.Loading())
val response = repo.getAllJobsByAPI()
jobsListUpdate.postValue(handleBreakingNewsResponse(response))
}
}
The above code works. However the problem arises when I call it first from a non-suspended function.
@HiltViewModel
class MainViewModel @Inject constructor(repo: JobsRepository) : ViewModel(){
@Inject lateinit var repo : JobsRepository
lateinit var jobDao: JobDao
val jobsListUpdate: MutableLiveData<Resource<JobsResponse>> = MutableLiveData()
var jobsListResponse: JobsResponse? = null
init {
jobDao = repo.getDao()
getJobsList()
}
fun getJobsList() {
viewModelScope.launch {
getJobsListFromApi()
}
}
private suspend fun getJobsListFromApi() {
jobsListUpdate.postValue(Resource.Loading())
val response = repo.getAllJobsByAPI()
jobsListUpdate.postValue(handleBreakingNewsResponse(response))
}
It's almost like the repo object is being nullified somewhere else in the codeflow, like perhaps after a navigation fragment is attached elsewhere.
The difference is definitely that suspend keyword. I would say its a race condition but whats unusual is that the reference to the repo becomes null after it has a value.
Any ideas? What else would be useful to add to this post?
I think you're real problem is that you're using both constructor injection and field injection on the same thing. I'm surprised this even compiles tbh. Try changing it to:
@HiltViewModel
class MainViewModel
@Inject constructor(
private val repo: JobsRepository
) : ViewModel(){
val jobsListUpdate: MutableLiveData<Resource<JobsResponse>> = MutableLiveData()
init {
getJobsList()
}
fun getJobsList() {
viewModelScope.launch {
getJobsListFromApi()
}
}
private suspend fun getJobsListFromApi() {
jobsListUpdate.postValue(Resource.Loading())
val response = repo.getAllJobsByAPI()
jobsListUpdate.postValue(handleBreakingNewsResponse(response))
}
}
My guess is what you're seeing is caused by the above problem and isn't actually related to suspend functions.