Skip to content

Instantly share code, notes, and snippets.

@khunzohn
Created December 24, 2019 05:50
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 khunzohn/b77174bc28a44c3eeb958568b8e5f3d9 to your computer and use it in GitHub Desktop.
Save khunzohn/b77174bc28a44c3eeb958568b8e5f3d9 to your computer and use it in GitHub Desktop.
class FetchMacsUseCase constructor(
private val productRepository: ProductRepository
) {
fun run(): Observable<MacsPartialState> {
return productRepository.fetchMacs()
.toObservable()
.map { MacsPartialState.MacsResult(it) }
.cast(MacsPartialState::class.java)
.startWith(MacsPartialState.LoadingMacs)
.onErrorReturn { error -> MacsPartialState.LoadMacsError(error) }
}
}
class StreamMacsUseCase constructor(
private val productRepository: ProductRepository
) {
fun run(): Observable<MacsPartialState> {
return productRepository.streamMacs()
.map { MacsPartialState.MacsResult(it) }
// loading is not required for streaming data from db and let's assume there's no error case
}
}
interface ProductRepository {
fun fetchMacs(): Single<List<Mac>>
fun streamMacs(): Observable<List<Mac>>
}
class ProductRepositoryImpl constructor(
private val productRemote: ProductRemote,
private val productLocal: ProductLocal
) : ProductRepository {
override fun fetchMacs(): Single<List<Mac>> {
return productRemote.getMacs()
.doOnSuccess { macs ->
productLocal.saveMacs(macs)
}
}
override fun streamMacs(): Observable<List<Mac>> {
return productLocal.streamMacs()
}
}
interface ProductRemote {
fun getMacs(): Single<List<Mac>>
}
class ProductRemoteImpl(private val mapper: MacMapper) : ProductRemote {
override fun getMacs(): Single<List<Mac>> {
TODO("Implementation detail for fetching macs from network")
// Mapping from network model to domain model happens here
}
}
interface ProductLocal {
fun saveMacs(macs: List<Mac>)
fun streamMacs(): Observable<List<Mac>>
}
class ProductLocalImpl(
private val macDao: MacDao,
private val mapper: MacMapper
) : ProductLocal {
override fun saveMacs(macs: List<Mac>) {
val entities: List<MacEntity> = macs.map { mapper.domainToEntity(it) }
TODO("Implementation detail for saving mac entities to the local db")
}
override fun streamMacs(): Observable<List<Mac>> {
return macDao.stream().map { macEntity ->
mapper.entityToDomain(macEntity)
}
}
}
class MacMapper {
fun networkToDomain(network: MacData): Mac {
TODO()
}
fun domainToEntity(domain: Mac): MacEntity {
TODO()
}
fun entityToDomain(entity: MacEntity): Mac {
TODO()
}
}
//domain model
data class Mac(
val id: String,
val imageUrl: String,
val name: String
)
// db model
data class MacEntity(
val id: String,
val imageUrl: String,
val name: String
)
//network model
data class MacData(
val id: String,
val imageUrl: String,
val name: String
)
class MacsViewModel(
private val streamMacsUseCase: StreamMacsUseCase,
private val fetchMacsUseCase: FetchMacsUseCase
) {
// intent fired by user or view
private val streamMacsIntent = PublishSubject.create<Any>()
private val fetchMacsIntent = PublishSubject.create<Any>()
//this will be subscribed by the View
private val viewStateSubject = BehaviorSubject.create<MacsViewState>()
fun handleIntent() {
val streamMacsPartialState: Observable<MacsPartialState> = streamMacsIntent
.switchMap { streamMacsUseCase.run() }
val fetchMacsPartialState: Observable<MacsPartialState> = fetchMacsIntent
.switchMap { fetchMacsUseCase.run() }
Observable.merge(
streamMacsPartialState,
fetchMacsPartialState
)
.scan(MacsViewState()) { oldState: MacsViewState, partialState: MacsPartialState ->
partialState.reduce(oldState)
}
.distinctUntilChanged()
.subscribe {
viewStateSubject.onNext(it)
}
}
}
data class MacsViewState(
val macs: List<Mac> = emptyList(),
val loadingMacs: Boolean = false,
val loadMacsError: Throwable? = null
)
sealed class MacsPartialState {
abstract fun reduce(oldState: MacsViewState): MacsViewState
object LoadingMacs : MacsPartialState() {
override fun reduce(oldState: MacsViewState): MacsViewState {
return MacsViewState(
macs = oldState.macs,
loadingMacs = true,
loadMacsError = oldState.loadMacsError
)
}
}
object MacsLoaded : MacsPartialState() {
override fun reduce(oldState: MacsViewState): MacsViewState {
return MacsViewState(
macs = oldState.macs,
loadingMacs = false,
loadMacsError = oldState.loadMacsError
)
}
}
data class MacsResult(val macs: List<Mac>) : MacsPartialState() {
override fun reduce(oldState: MacsViewState): MacsViewState {
return MacsViewState(
macs = macs,
loadingMacs = oldState.loadingMacs,
loadMacsError = oldState.loadMacsError
)
}
}
data class LoadMacsError(val error: Throwable) : MacsPartialState() {
override fun reduce(oldState: MacsViewState): MacsViewState {
return MacsViewState(
macs = oldState.macs,
loadingMacs = oldState.loadingMacs,
loadMacsError = error
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment