pie
pie

Reputation: 163

LiveData Observer

In parallel, I download data from a local source and check for updates on the server. But for some reason, the sheet data is not updated. I use Firestore.

Fragment:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        checkUpdate()
        getData()
    }

    private fun checkUpdate() {
        viewModel.checkUpdate().observe(viewLifecycleOwner, Observer { result ->
            when(result) {
                is Response.Loading -> { loading() }
                is Response.Success -> {
                    viewModel.updateData()
                }
                is Response.Error -> { error(result.exception) }
            }
        })
    }

    private fun getData() {
        viewModel.getData().observe(viewLifecycleOwner, Observer { result ->
            when(result) {
                is Response.Loading -> { loading() }
                is Response.Success -> { success(result.data) }
                is Response.Error -> { error(result.exception) }
            }
        })
    }

ViewModel

    private var data: LiveData<Response<List<Rule>>>

    init {
        data = loadData()
    }

    fun getData() = data

    fun updateData() { data = loadData() }

    private fun loadData() =
        liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Response.Loading)

            val result = repository.getData()
            if (result is Response.Success || result is Response.Error) emit(result)
        }

    fun checkUpdate() =
        liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            val lastUpdateTimestamp = Timestamp(
                preferences.getLong("PREF_RULE_LAST_UPDATE", 0),
                0
            )

            emit(Response.Loading)

            when(val result = repository.checkUpdate(lastUpdateTimestamp)) {
                is Response.Success -> {
                    if (result.data > lastUpdateTimestamp) {
                        try {
                            val editor = preferences.edit()
                            editor.putLong("PREF_RULE_LAST_UPDATE", result.data.seconds)
                            editor.apply()
                            emit(Response.Success(true))
                        } catch (exception: Exception) {
                            emit(Response.Error(exception))
                        }
                    } else {
                        emit(Response.Success(false))
                    }
                }
                is Response.Error -> { emit(result) }
            }
        }

Repository

    suspend fun getData(): Response<List<Rule>> =
        suspendCoroutine { continuation ->
            firebaseFirestore
                .collection(COLLECTION_NAME)
                .whereEqualTo("published", true)
                .orderBy("number", Query.Direction.ASCENDING)
                .get(Source.CACHE)
                .addOnSuccessListener { query ->
                    try {
                        val data = arrayListOf<Rule>()
                        query.documents.forEach {document ->
                            document.toObject(RuleDomain::class.java)?.let {
                                it.id = document.id
                                data.add(it.toRule())
                            }
                        }
                        continuation.resume(Response.Success(data))
                    } catch (exception: Exception) {
                        Log.e(TAG, exception.localizedMessage!!)
                        continuation.resume(Response.Error(exception))
                    }
                }
                .addOnFailureListener { exception ->
                    Log.e(TAG, exception.localizedMessage!!)
                    continuation.resume(Response.Error(exception))
                }
        }

    suspend fun checkUpdate(lastUpdateTimestamp: Timestamp): Response<Timestamp> =
        suspendCoroutine { continuation ->
            firebaseFirestore
                .collection(COLLECTION_NAME)
                .whereGreaterThan("update_timestamp", lastUpdateTimestamp)
                .orderBy("update_timestamp", Query.Direction.ASCENDING)
                .get(Source.SERVER)
                .addOnSuccessListener { query ->
                    try {
                        val data = query.documents.first()["update_timestamp"] as Timestamp
                        continuation.resume(Response.Success(data))
                    } catch (exception: Exception) {
                        Log.e(TAG, exception.localizedMessage!!)
                        continuation.resume(Response.Error(exception))
                    }
                }
                .addOnFailureListener { exception ->
                    Log.e(TAG, exception.localizedMessage!!)
                    continuation.resume(Response.Error(exception))
                }
        }

I want to replace that the "data" variable is actually being updated since the data appears when the screen is rotated. As far as I know from the documentation viewModel.getData().observe(viewLifecycleOwner, Observer { ... }) should be signed while the fragment is alive, and should commit all changes.

Upvotes: 0

Views: 99

Answers (1)

pie
pie

Reputation: 163

I decided to completely abandon part of the code and rewrite it a bit.

Fragment

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        getData()
    }

    private fun getData() {
        viewModel.data.observe(viewLifecycleOwner, Observer { result ->
            when(result) {
                is Response.Loading -> { loading() }
                is Response.Success -> { success(result.data) }
                is Response.Error -> { error(result.exception) }
            }
        })
    }

ViewModel

    private var _data: LiveData<Response<List<Rule>>>
    val data: LiveData<Response<List<Rule>>>
        get() = _data

    init {
        _data = loadData()
    }

    private fun loadData(): LiveData<Response<List<Rule>>> =
        liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Response.Loading)

            val lastUpdateTimestamp = Timestamp(preferences.getLong("PREF_RULE_LAST_UPDATE", 0), 0)
            val response = repository.getData(lastUpdateTimestamp)

            if (response is Response.Success) {
                val result = response.data

                if (result.containsKey("LAST_UPDATE_TIMESTAMP"))
                    if (result["LAST_UPDATE_TIMESTAMP"] as Timestamp > lastUpdateTimestamp)
                        preferences.put("PREF_RULE_LAST_UPDATE", (result["LAST_UPDATE_TIMESTAMP"] as Timestamp).seconds)

                if (result.containsKey("DATA"))
                    emit(Response.Success(result["DATA"] as List<Rule>))
            }

            if (response is Response.Error)
                emit(response)
        }

Repository

    suspend fun getData(timestamp: Timestamp): Response<HashMap<String, Any?>> =
        suspendCoroutine { continuation ->
            firebaseFirestore
                .collection(COLLECTION_NAME)
                .whereGreaterThan("update_timestamp", timestamp)
                .orderBy("update_timestamp", Query.Direction.DESCENDING)
                .get(Source.SERVER)
                .addOnCompleteListener { queryServer ->
                    val hashMap = HashMap<String, Any?>()
                    if (!queryServer.result?.isEmpty!!)
                        hashMap["LAST_UPDATE_TIMESTAMP"] = queryServer.result!!.first().get("update_timestamp") as Timestamp

                    firebaseFirestore
                        .collection(COLLECTION_NAME)
                        .whereEqualTo("published", true)
                        .orderBy("number", Query.Direction.ASCENDING)
                        .get(Source.CACHE)
                        .addOnSuccessListener { queryCache ->
                            try {
                                val data = arrayListOf<Rule>()
                                queryCache.documents.forEach {document ->
                                    document.toObject(RuleDomain::class.java)?.let {
                                        it.id = document.id
                                        data.add(it.toRule())
                                    }
                                }
                                hashMap["DATA"] = data

                                continuation.resume(Response.Success(hashMap))
                            } catch (exception: Exception) {
                                Log.e(TAG, exception.localizedMessage!!)
                                continuation.resume(Response.Error(exception))
                            }
                        }
                        .addOnFailureListener { exception ->
                            Log.e(TAG, exception.localizedMessage!!)
                            continuation.resume(Response.Error(exception))
                        }
                }
        }

Upvotes: 1

Related Questions