Last active
August 7, 2020 16:09
-
-
Save svasquezm/18e50d4eac98358f788b43387efe18cd to your computer and use it in GitHub Desktop.
Flow data with Events Approach
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.lifecycle.MutableLiveData | |
import androidx.lifecycle.Observer | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.viewModelScope | |
import kotlinx.coroutines.delay | |
import kotlinx.coroutines.flow.collect | |
import kotlinx.coroutines.flow.flow | |
import kotlinx.coroutines.launch | |
import kotlin.random.Random | |
/** | |
* Events to be emitted on Data level | |
*/ | |
sealed class Event<T> { | |
class Loading<T>: Event<T>() | |
class Success<T>(val data: T): Event<T>() | |
sealed class Error<T>: Event<T>() { | |
class NotFound<T>: Error<T>() | |
class InternalServer<T>: Error<T>() | |
class Unauthenticated<T>: Error<T>() | |
} | |
} | |
// DATA: Generic repository | |
/** | |
* Handles data from server and locally. | |
* A Mixed approach can be achieved using fetchUsers(false) | |
*/ | |
class Repository( | |
private val dummyUserApiService: DummyUserApiService, | |
private val dummyUserDao: DummyUserDAO | |
) { | |
/** | |
* Retrieves an user locally | |
*/ | |
suspend fun getUsers() = flow<Event<List<User>>> { | |
dummyUserDao.getUsers() | |
.takeIf { it.isNotEmpty() } | |
?.let { users -> emit(Event.Success(users)) } | |
?: emit(Event.Error.NotFound()) | |
} | |
/** | |
* Retrieves users from API | |
*/ | |
suspend fun fetchUsers(serverOnly: Boolean = false) = flow<Event<List<User>>> { | |
val result = dummyUserApiService.fetchUsers() | |
if(result.statusOk){ | |
insert(result.body) | |
if(serverOnly) { | |
if(result.body == null){ | |
emit(Event.Error.NotFound()) | |
} else { | |
emit(Event.Success(result.body)) | |
} | |
} else { | |
getUsers() | |
} | |
} else { | |
emit(Event.Error.Unauthenticated()) | |
} | |
} | |
private fun insert(users: List<User>?){ | |
users?.let { dummyUserDao.insert(it) } | |
} | |
} | |
// PRESENTATION: Generic ViewModel | |
class UserViewModel(private val repo: Repository) : ViewModel(){ | |
val userLiveData = MutableLiveData<Event<List<User>>>() | |
/** | |
* From DB approach | |
*/ | |
fun getUsers(){ | |
viewModelScope.launch { | |
repo.getUsers().collect { | |
userLiveData.value = it | |
} | |
} | |
} | |
/** | |
* From Network approach (can return Event.Error objects) | |
*/ | |
fun fetchUsers() { | |
viewModelScope.launch { | |
repo.fetchUsers().collect { | |
userLiveData.value = it | |
} | |
} | |
} | |
} | |
// Dummy classes (a retrofit service like) | |
class DummyUserApiService { | |
data class ApiServiceResult<N>(val statusOk: Boolean, val body: N? = null) | |
suspend fun fetchUsers(): ApiServiceResult<List<User>> { | |
delay(3000) | |
return if(Random.nextBoolean()){ | |
ApiServiceResult(true, | |
listOf( | |
User(1, "Andrecito"), | |
User(2, "Juancito") | |
) | |
) | |
} else { | |
ApiServiceResult(false) | |
} | |
} | |
} | |
class DummyUserDAO { | |
val usersInFakeDB = mutableListOf( | |
User(0, "Jorgito") | |
) | |
fun getUsers(): List<User> = usersInFakeDB | |
fun insert(users: List<User>) = usersInFakeDB.addAll(users) | |
} | |
data class User(val id: Int, val name: String) | |
// Cases | |
// Next lines simulates we are in an Activity / Fragment component | |
val dummyUserDAO = DummyUserDAO() | |
val dummyUserApiService = DummyUserApiService() | |
val repo = Repository(dummyUserApiService, dummyUserDAO) | |
val viewModel = UserViewModel(repo) | |
// Owner should be the activity or fragment it self | |
viewModel.userLiveData.observe(owner, Observer { | |
when(it) { | |
is Event.Success -> { | |
// it.data contains retrieved users | |
} | |
is Event.Loading -> { | |
// Shows a Progressbar or something | |
} | |
is Event.Error.Unauthenticated -> { | |
// Handle 401 Error | |
} | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment