0

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.

1 Answer 1

1

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))
                        }
                }
        }
Sign up to request clarification or add additional context in comments.

1 Comment

If there is no network connection, an exception is thrown when using Source.SERVER. Therefore, it is probably more correct to replace Source.SERVER with Source.DEFAULT. In any case, Firestore prefers actual data from the server.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.