Reputation: 9481
I am trying to use LiveData and Coroutines together in MVVM, and I may be missing something simple.
class WeatherViewModel (
private val weatherRepository: ForecastRepository
) : ViewModel() {
var weather: LiveData<Weather>;
/**
* Cancel all coroutines when the ViewModel is cleared.
*/
@ExperimentalCoroutinesApi
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
init {
viewModelScope.launch {
weather = weatherRepository.getWeather()
}
}
}
But I am getting Property must be initialized or be abstract
on assigning the weather
in the init
function.
I am assuming this is the case because I am using coroutines viewModelScope.launch
.
override suspend fun getWeather(): LiveData<Weather> {
return withContext(IO){
initWeatherData()
return@withContext weatherDao.getWeather()
}
}
How do I fix this?
Upvotes: 5
Views: 1972
Reputation: 14233
In Kotlin, by default, every property must be initialized (even the nullable types with null).
You can initialize your property directly in the declaration or within init block.
Problem with your code is that the launch function can keep on going after your init block is done, and the compiler knows this. So, it's telling you - don't count on it.
As previously said, you can use lateinit to state that you'll initialize your property later, if you're sure it will happen before you're gonna use it.
Upvotes: 0
Reputation: 30735
You can declare weather
property as lateinit
:
private lateinit var weather: LiveData<String>
Or make it nullable:
private var weather: LiveData<String>? = null
If you're sure that the property will be initialized before you first use it use lateinit
otherwise make it nullable.
Upvotes: 2
Reputation: 1676
weather
must be initialized with instantiation of the class because you have not said that it can be null, and you are not using the lateinit
keyword (which you should not in this case).
launch
is an async coroutine call that returns immediately, but will be executed at some point in the future. This means that your init
block completes and returns without weather
being initialized.
Use runBlocking
instead. This will block until you have the result in the init
block and so guarantee that weather is not null on instantiation. Something like:
init {
weather = runBlocking {
weatherRepository.getWeather()
}
}
You can pass which ever coroutine context dispatcher to runBlocking
as well.
Or - stick with the coroutine, but join in the init block like:
init {
val job = viewModelScope.launch {
weather = weatherRepository.getWeather()
}
job.join()
}
Upvotes: -1
Reputation: 2427
Change the signature as follow:
var weather = MutableLiveData<Weather>();
Plus, you should return just a Weather
object and not a LiveData<>
so you should change the signature of getWeather()
to return : Weather
.
Upvotes: 0