Skip to content

Instantly share code, notes, and snippets.

@ZakTaccardi
Last active March 23, 2018 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZakTaccardi/a854c79f140f8b18b202a3768b5874eb to your computer and use it in GitHub Desktop.
Save ZakTaccardi/a854c79f140f8b18b202a3768b5874eb to your computer and use it in GitHub Desktop.
Reactive Login
import android.app.Activity
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.subscribeBy
/**
* Stores state related to login.
*/
interface LoginRepository {
// A stream of login states
fun stateStream(): Observable<LoginState>
// one way to log in
fun submitCredentials(username: String, password: String)
// another way to login
fun submitFingerPrint(isValid: Boolean)
}
/**
* Represents state of the Logged in User.
*
* - bonus points: log the state to the console for easy debugging
*/
data class LoginState(
val isLoggedIn: Boolean,
val isAuthorizing: Boolean // true when network call to validate credentials is in progress
)
class LoginActivity : Activity() {
private lateinit var loginRepository: LoginRepository
private val startDisposables = CompositeDisposable()
override fun onStart() {
super.onStart()
// when the user is logged in, go to the main activity
loginRepository.stateStream()
.filter { state: LoginState -> state.isLoggedIn }
.firstOrError()
.subscribeBy { goToMainActivity() }
.addTo(startDisposables) // unsubscribe in onStop()
loginRepository.stateStream()
.map { state: LoginState -> state.isAuthorizing }
.filter { isAuthorizing: Boolean -> isAuthorizing == true }
.distinctUntilChanged() // ignore duplicate emissions
.subscribeBy { isAuthorizingInProgress: Boolean ->
if (isAuthorizingInProgress) {
enableInput(false) // disable input while network call is running
showLoadingProgressBar(true)
} else {
enableInput(true) // enable input while network call is not running
showLoadingProgressBar(false)
}
}
.addTo(startDisposables) // avoid memory leak
}
override fun onStop() {
startDisposables.clear() // avoid memory leaks
super.onStop()
}
fun onClickLogin() {
val username = "" // extract username
val password = "" // extract password
// this is void. we don't need a return value because we are already observing the login
// state in `onStart()` to go to the MainActivity.
// - bonus points: no memory leaks
loginRepository.submitCredentials(username, password)
}
fun goToMainActivity() {
// fire intent..
}
fun onFingerprint(isFingerprintValid: Boolean) {
// we've now added a second way to login the user.
// - notice we don't have to add new code to handle the result, because it's already being
// observed in `onStart()`
// - imagine this was part of a child fragment instead. no need to grab a reference to the
// parent activity and do ugly casting to call `goToMainActivity()`
loginRepository.submitFingerPrint(isFingerprintValid)
}
fun enableInput(enable: Boolean = true) {
if (enable) {
// enable user input
} else {
// disable user input
}
}
fun showLoadingProgressBar(show: Boolean = true) {
if (show) {
// show loading indicator in UI
} else {
// hide loading indicator
}
}
}
// A base activity for activities that requires user to be logged in
abstract class BaseRequiresAuthActivity : Activity() {
lateinit var loginRepository: LoginRepository
private val startDisposables = CompositeDisposable()
override fun onStart() {
super.onStart()
// launch login activity when user is logged out
loginRepository.stateStream()
.filter { state -> !state.isLoggedIn }
.firstOrError()
.subscribeBy { goToLoginActivity() }
.addTo(startDisposables)
}
fun goToLoginActivity() {
// finish this activity, fire login activity intent
}
}
@ZakTaccardi
Copy link
Author

ZakTaccardi commented Mar 23, 2018

LoginRepository stores LoginState, which activities observe. UI can request to update the LoginState by sending input that the LoginRepository controls

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