Skip to content

Instantly share code, notes, and snippets.

@JorgeCastilloPrz
Created July 1, 2020 07:53
Show Gist options
  • Save JorgeCastilloPrz/dca5fe92016ea4cce80a3c5cf6a1a2e3 to your computer and use it in GitHub Desktop.
Save JorgeCastilloPrz/dca5fe92016ea4cce80a3c5cf6a1a2e3 to your computer and use it in GitHub Desktop.
Functional Android workshop exercise solutions
// Persistence.kt will end up looking like this after exercises 1 and 2.
fun stubPersistence(defaultAccounts: List<User>? = null, pool: CoroutineContext = IOPool) =
object : AccountPersistence {
private var accounts = defaultAccounts ?: emptyList()
private lateinit var updateTrigger: () -> Unit
override fun loadAccountsFromDatabase(): Stream<List<User>> = Stream(
Stream.async {
updateTrigger = { emit(accounts); }
},
Stream.effect {
evalOn(pool) {
accounts // this would be an effect to connect to the db and access accounts irl.
}
}.delayBy(700.milliseconds)
).parJoinUnbounded()
override fun dbPool() = pool
override suspend fun updateAccounts(accounts: List<User>) {
this.accounts = accounts
}
override suspend fun invalidateDatabase() {
accounts = emptyList()
updateTrigger()
}
override suspend fun toggleFavInDatabase(user: User): Unit {
accounts = accounts.map {
if (it.id == user.id) user.copy(isFavorite = !user.isFavorite) else it
}
updateTrigger()
}
}
// loadAccountsRepo() will ultimately look like this after exercise 3
/**
* This needs to load accounts from DB fist, and in case there's an error or the DB accounts list
* is empty, fallback to loading accounts from network.
*/
fun HomeDependencies.loadAccountsRepo(): Stream<List<User>> =
loadAccountsFromDatabase()
.handleErrorWith { fetchAccountsFromNetwork() }
.flatMap {
if (it.isEmpty()) {
fetchAccountsFromNetwork()
} else {
Stream(it)
}
}.effectTap { updateAccounts(it) }
// home.kt (program()) will end up looking like this after exercises 4, 5 and 6
/*
Our home program exist out of refreshing items on P2R & loading initial data.
*/
fun HomeDependencies.program(render: Stream<Unit>): Stream<Unit> = Stream(
loadAccounts(),
pullToRefresh().effectMap { invalidateDatabase() },
favClicks().effectMap { toggleFavInDatabase(it) },
render
).parJoinUnbounded()
fun HomeDependencies.loadAccounts(): Stream<Unit> =
post(HomeViewState.Loading)
.flatMap { loadAccountsRepo() }
.map(HomeViewState::Content).flatMap { post(it) }
.handleErrorWith { post(HomeViewState.Error(it)) }
@matiaslev
Copy link

Hi! and thanks for the workshop!

Just a detail that could help someone :)
I think that the favClicks Stream should be passed as a parameter at least for the version of the code that we use.

fun HomeDependencies.program(render: Stream<Unit>, favClicks: Stream<User>): Stream<Unit> = Stream(
        loadAccounts(),
        pullToRefresh().effectMap { invalidateDatabase() },
        favClicks.effectMap { toggleFavInDatabase(it) },
        render
).parJoinUnbounded()

fun HomeDependencies.loadAccounts(): Stream<Unit> =
        post(HomeViewState.Loading)
                .flatMap { loadAccountsRepo() }
                .map(HomeViewState::Content).flatMap { post(it) }
                .handleErrorWith { post(HomeViewState.Error(it)) }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment