Psijic
Psijic

Reputation: 992

Return data from Repository to ViewModel without LiveData

I'm just trying to find an answer how to pass the data from Repository to ViewModel without extra dependencies like RxJava. The LiveData seems as a not good solution here because I don't need to proceed it in my Presentation, only in ViewModel and it's not a good practice to use observeForever. The code is simple: I use Firebase example trying to pass data with Flow but can't use it within a listener (Suspension functions can be called only within coroutine body error):

Repository

    fun fetchFirebaseFlow(): Flow<List<MyData>?> = flow {
        var ret: List<MyData>? = null
        firebaseDb.child("data").addListenerForSingleValueEvent(
            object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val data = dataSnapshot.getValue<List<MyData>>()
                    emit(data) // Error. How to return the data here?
                }

                override fun onCancelled(databaseError: DatabaseError) {
                    emit(databaseError) // Error. How to return the data here?
                }
            })
//        emit(ret) // Useless here
    }

ViewModel

    private suspend fun fetchFirebase() {
        repo.fetchFirebaseFlow().collect { data ->
            if (!data.isNullOrEmpty()) {
                // Add data to something
            } else {
                // Something else
            }
    }

Upvotes: 1

Views: 1260

Answers (2)

Psijic
Psijic

Reputation: 992

ObservableField is like LiveData but not lifecycle-aware and may be used instead of creating an Observable object.

{
    val data = repo.getObservable()

    val cb = object : Observable.OnPropertyChangedCallback() {
        override fun onPropertyChanged(observable: Observable, i: Int) {
            observable.removeOnPropertyChangedCallback(this)
            val neededData = (observable as ObservableField<*>).get()
        }
    }

    data.addOnPropertyChangedCallback(cb)
}


fun getObservable(): ObservableField<List<MyData>> {
    val ret = ObservableField<List<MyData>>()
    firebaseDb.child("events").addListenerForSingleValueEvent(
        object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                ret.set(dataSnapshot.getValue<List<MyData>>())
            }

            override fun onCancelled(databaseError: DatabaseError) {
                ret.set(null)
            }
        })
    return ret
}

It is also possible to use suspendCancellableCoroutine for a single result. Thanks to Kotlin forum.

Upvotes: 0

iamanbansal
iamanbansal

Reputation: 2742

You can use callbackFlow

@ExperimentalCoroutinesApi
    fun fetchFirebaseFlow(): Flow<List<String>?> = callbackFlow {
        
        val listener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val data = dataSnapshot.getValue<List<MyData>>()
                offer(data)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                
            }
        }
        val ref =firebaseDb.child("data")
        reef.addListenerForSingleValueEvent(listener)

        awaitClose{
            //remove listener here
           ref.removeEventListener(listener)
        }
    }

Upvotes: 3

Related Questions