-
Notifications
You must be signed in to change notification settings - Fork 36
3. Changes
A Change is a result of transforming an Action. When loading notes, for instance, the following changes can occur:
- loading change
- success (notes data) or error change
Each Action can produce one or more Changes. Normally (but not always), a Change is a result of interacting with the Domain layer.
Changes are implemented as Kotlin sealed class
es. In the above example, we can represent the Changes as follows:
sealed class Change {
object Loading : Change()
data class Notes(val notes: List<Note>) : Change()
data class Error(val throwable: Throwable?) : Change()
}
The ViewModel will transform this Action into Changes as follows:
val loadNotesChange = actions.ofType<Action.LoadNotes>()
.switchMap {
loadNoteListUseCase.loadAll()
.subscribeOn(Schedulers.io())
.toObservable()
.map<Change> { Change.Notes(it) }
.defaultIfEmpty(Change.Notes(emptyList()))
.onErrorReturn { Change.Error(it) }
.startWith(Change.Loading)
}
Note how powerful (yet concise and expressive) the RxJava chain above is. We load notes from the Doman layer. Before we begin, we emit a Loading Change. If there are no notes found, we emit an empty list. If we encounter an error, we emit an Error Change. All of that in just a few lines of composable functional code!
Most of the time, your ViewModels will contain multiple RxJava chains generating Changes. For instance, the sample app defines several changes in the NoteDetailViewModel
:
val loadNoteChange = actions.ofType<Action.LoadNoteDetail>()
.switchMap { action ->
noteDetailUseCase.findById(action.noteId)
.subscribeOn(Schedulers.io())
.toObservable()
.map<Change> { Change.NoteDetail(it) }
.onErrorReturn { Change.NoteLoadError(it) }
.startWith(Change.Loading)
}
val deleteNoteChange = actions.ofType<Action.DeleteNote>()
.switchMap { action ->
noteDetailUseCase.findById(action.noteId)
.subscribeOn(Schedulers.io())
.flatMapCompletable { deleteNoteUseCase.delete(it) }
.toSingleDefault<Change>(Change.NoteDeleted)
.onErrorReturn { Change.NoteDeleteError(it) }
.toObservable()
.startWith(Change.Loading)
}
Later we merge them into a single stream to be sent to the Reducer:
val allChanges = Observable.merge(loadNoteChange, deleteNoteChange)