Skip to content

Instantly share code, notes, and snippets.

@GeekTree0101
Last active August 6, 2019 00:50
Show Gist options
  • Save GeekTree0101/7fa26211fd4552f3ab60c3ba290c8b4f to your computer and use it in GitHub Desktop.
Save GeekTree0101/7fa26211fd4552f3ab60c3ba290c8b4f to your computer and use it in GitHub Desktop.
상태기반 ViewModel 과 Repository & DataSource 활용.
import RxSwift
import RxCocoa
// MARK: - Action
extension Reactive where Base: TestViewModel {
var refresh: Binder<Void> {
return Binder(base) { viewModel, _ in
guard let id = viewModel.viewState.value?.id else { return }
viewModel.repository.refresh(id)
}
}
}
// MARK: - State
struct TestViewState {
var id: Int
var title: String
static let defaultState = TestViewState.init(id: -1, title: "")
}
// MARK: - ViewModel
class TestViewModel: ReactiveCompatible {
typealias ViewState = TestViewState
// MARK: State
public let viewState: ReactiveState<ViewState> = .init()
public lazy var error: Observable<Error> = {
return self.repository.error.share(replay: 1, scope: .whileConnected)
}()
private let disposeBag = DisposeBag()
fileprivate let repository: StoreRepository
init(repo: StoreRepository) {
self.repository = repo
repository.getStore()
.mutation(viewState, { context in
var (res, viewState) = context
viewState?.title = res?.name ?? ""
return viewState
})
.disposed(by: disposeBag)
}
}
class StoreRepository {
var remote = StoreRemoteDataSource.init()
var cache = StoreCachedDataSource.init()
var error = PublishRelay<Error>.init()
private var disposeBag = DisposeBag()
public func refresh(_ id: Int) {
remote.getStore(storeID: id)
.subscribe(onSuccess: { [weak self] store in
self?.cache.setStore(store)
}, onError: { [weak self] error in
self?.error.accept(error)
})
.dispose()
}
public func getStore() -> Observable<Store?> {
return cache.getStore()
}
}
class StoreRemoteDataSource {
func getStore(storeID: Int) -> Single<Store> {
return Single.just(Store(id: storeID, name: "apple store", productDescription: "iphone"))
}
}
class StoreCachedDataSource {
private let cachedStore = BehaviorRelay<Store?>.init(value: nil)
public func setStore(_ store: Store) {
cachedStore.accept(store)
}
public func getStore() -> Observable<Store?> {
return cachedStore.asObservable()
}
public func clear() {
cachedStore.accept(nil)
}
}
public class ReactiveState<Base> {
private let store = BehaviorRelay<Base?>.init(value: nil)
public let disposeBag = DisposeBag()
public var getter: Observable<Base?> {
return store.asObservable()
}
public var value: Base? {
get {
return store.value
}
set {
store.accept(newValue)
}
}
}
extension Observable {
public func mutation<Base>(_ reactiveState: ReactiveState<Base>,
_ mutation: @escaping (((E, Base?)) -> Base?)) -> Disposable {
weak var weakState = reactiveState
return self.withLatestFrom(reactiveState.getter) { ($0, $1) }
.map(mutation)
.subscribe(onNext: { newValue in
weakState?.value = newValue
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment