Created
July 20, 2019 08:55
-
-
Save Tadas44/d5f10ac4c1fcf64ceed83148f3cc29fc to your computer and use it in GitHub Desktop.
RxJava MVVM example
This file contains hidden or 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
open class BaseFragment : Fragment() { | |
private var disposable = CompositeDisposable() | |
fun Disposable.addToDisposable() { | |
disposable.add(this) | |
} | |
override fun onDestroyView() { | |
super.onDestroyView() | |
disposable.clear() | |
} | |
fun getMainActivity() = activity as MainActivity | |
fun Observable<StateTracker.State>.showProgress(): Observable<StateTracker.State> { | |
val progress = ProgressDialog(context) | |
progress.setTitle(R.string.progress_title) | |
progress.setMessage(getString(R.string.progress_message)) | |
progress.setCancelable(false) // disable dismiss by tapping outside of the dialog | |
progress.dismiss() | |
return observeOn(RxSchedulers.uiScheduler) | |
.doOnNext { | |
when (it) { | |
StateTracker.State.Loading -> progress.show() | |
else -> progress.dismiss() | |
} | |
} | |
} | |
fun Observable<StateTracker.State>.showError(): Observable<StateTracker.State> { | |
return observeOn(RxSchedulers.uiScheduler) | |
.doOnNext { | |
when (it) { | |
is StateTracker.State.Error -> showError(it.error) | |
} | |
} | |
} | |
private fun showError(error: Throwable) { | |
val message = (error as? ApiError)?.message ?: getString(R.string.error_title) | |
val dialog = MaterialAlertDialogBuilder(context) | |
.setMessage(message) | |
.show() | |
} | |
fun <T> Observable<T>.showApiError(apiErrorBlock: (String) -> Unit) = | |
showApiError(apiErrorBlock, {}) | |
fun <T> Observable<T>.showApiError(apiErrorBlock: (String) -> Unit, errorBlock: () -> Unit = {}): Observable<T> { | |
return doOnNext { state -> | |
when (state) { | |
is StateTracker.State.Error -> { | |
when (state.error) { | |
is ApiError -> { | |
state.error.message?.run { | |
apiErrorBlock.invoke(this) | |
} | |
} | |
else -> { | |
errorBlock.invoke() | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or 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
class RegisterFragment : BaseFragment() { | |
private val viewModel: RegisterViewModel by viewModel() | |
private val smsRetriever by lazy { | |
SmsRetriever.getClient(activity!!) | |
} | |
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | |
return inflater.inflate(R.layout.fragment_register, container, false) | |
} | |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
super.onViewCreated(view, savedInstanceState) | |
viewModel.navigator = DefaultRegisterNavigator(findNavController()) | |
bindViewModel() | |
} | |
private fun bindViewModel() { | |
val register = register_continue_button.clicks() | |
val textInput = register_text_input.textInputEditText.textChanges() | |
.skipInitialValue() | |
.doOnNext { register_text_input.textInputLayout.error = null } | |
val input = RegisterViewModel.Input(textInput, register) | |
val output = viewModel.transform(input) | |
output.register | |
.observeOn(RxSchedulers.uiScheduler) | |
.subscribe { smsRetriever.startSmsRetriever() } | |
.addToDisposable() | |
output.enabled | |
.subscribe { enabled -> | |
register_continue_button.isEnabled = enabled | |
}.addToDisposable() | |
output.state | |
.showProgress() | |
.showApiError { | |
showValidationDialog(it) | |
showInputMessage(it) | |
} | |
.subscribe() | |
.addToDisposable() | |
} | |
fun showValidationDialog(message: String) { | |
val dialog = MaterialAlertDialogBuilder(context) | |
.setTitle(R.string.register_error_alert_title) | |
.setMessage(message) | |
.setPositiveButton(R.string.button_ok) { dialog, _ -> | |
dialog.dismiss() | |
} | |
.show() | |
} | |
private fun showInputMessage(message: String) { | |
//using hardcoded error | |
register_text_input.textInputLayout.error = getString(R.string.register_error_text) | |
} | |
} |
This file contains hidden or 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
class RegisterViewModel(val registerUseCase: RegisterUseCase) : ViewModel() { | |
lateinit var navigator: RegisterNavigator | |
data class Input( | |
val number: Observable<CharSequence>, | |
val register: Observable<Unit> | |
) | |
data class Output( | |
val state: Observable<StateTracker.State>, | |
val enabled: Observable<Boolean>, | |
val register: Observable<Unit> | |
) | |
fun transform(input: Input): Output { | |
val stateTracker = StateTracker() | |
val enabled = input.number | |
.map { !it.isNullOrEmpty() } | |
val register = input.register | |
.withLatestFrom(input.number) | |
.map { it.second.toString() } | |
.flatMap { registerUseCase.register(it).toObservable().track(stateTracker) } | |
.withLatestFrom(input.number) | |
.map { it.second.toString() } | |
.doOnNext { navigator.toConfirmCode(it) } | |
.mapToUnit() | |
val state = stateTracker.asObservable() | |
return Output(state, enabled, register) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More info about RxSwift https://github.com/sergdort/CleanArchitectureRxSwift