2

My app displays an object queried with Retrofit that a user can save to the local Room database by tapping an icon. The icon itself works, but if I restart the app the icon's boolean value is set to false even though the object is saved in the database.

How can I check if the object is already in the database and initialize the icon's state?

This is the code I have so far:

Icon Composable

@Composable
fun FavoriteButton(
    apod: Apod,
    viewModel: ApodViewModel,
    color: Color = Color.White
) {
    var isFavorite by remember { mutableStateOf(viewModel.apodExists.value) }

    IconToggleButton(
        checked = isFavorite,
        onCheckedChange = {
            isFavorite = !isFavorite
            if (isFavorite) {
                viewModel.addApod(apod)
            } else {
                viewModel.removeApod(apod)
            }
        }
    ) {
        Icon(
            tint = color,
            imageVector = if (isFavorite) {
                Icons.Filled.Favorite
            } else {
                Icons.Default.FavoriteBorder
            },
            contentDescription = null
        )
    }
}

ViewModel

@HiltViewModel
class ApodViewModel @Inject constructor(application: Application) : ViewModel() {

    private val readAllData: LiveData<List<Apod>>
    private val repository: ApodRepository

    private val _apodExists = MutableLiveData(false)
    val apodExists: LiveData<Boolean>
        get() = _apodExists

    init {
        val apodDao = ApodDatabase.getDatabase(application).apodDao()
        repository = ApodRepository(apodDao)
        readAllData = repository.readAllData
    }

    fun apodExists(apod: Apod) {
        _apodExists.postValue(repository.getApodByDate(apod.date) != null)
    }

    fun addApod(apod: Apod) {
        viewModelScope.launch(Dispatchers.IO) {
            if (repository.getApodByDate(apod.date) == null) {
                repository.addApod(apod)
                Log.d("ApodViewModel", "Apod saved to database")
            } else {
                Log.d("ApodViewModel", "Apod already exists in database")
            }
        }
    }

    fun removeApod(apod: Apod) {
        viewModelScope.launch(Dispatchers.IO) {
            if (repository.getApodByDate(apod.date) != null) {
                repository.removeApod(apod)
                Log.d("ApodViewModel", "Apod deleted from database")
            }
        }
    }

}

I'm struggeling with actually calling the apodExists() since I have to do it in the composable from the main thread. If I call it from the composable it keeps throwing me an error that I can't call it from the main thread. Where do I have to call apodExists(apod) to make it work?

Halil Ozel
  • 2,482
  • 3
  • 17
  • 32
  • 1
    Use `observeAsState` to collect live data value. Also I don't see any calls of `apodExists` that could've update it. – Phil Dukhov May 28 '22 at 05:03

1 Answers1

-1

You are expecting to read a value from the database synchronously, which is not something you should do.

This is the age old issue of asynchronous data loading - what you should do is expose a state from your viewmodel with a loading flag that is initially set to true, and then load the database entry in a coroutine. When you have loaded the value, you update your state to set the loading flag to false and provide the value that indicates if the database is populated or not.

In your composable you would check the value of the loading flag and display a progress indicator or any other placeholder of your choosing and, once the loading toggles to false you can update your UI to show the correct value based on the database read.

Francesc
  • 25,014
  • 10
  • 66
  • 84