Engineering

[Snippet] CycleViewModel

import RxSwift
import RxCocoa

public enum NoState {}
public enum NoActivity {}
public enum NoMutation {}
public enum NoAction {}

public protocol CycleViewModel : class {

  associatedtype State
  associatedtype Activity
  associatedtype Action
  associatedtype Mutation

  var activity: CycleEvent<Activity> { get }
  var state: StateVariable<State> { get }
  func mutate(_ action: Action) -> Mutation
  func reduce(_ mutation: Mutation)
}

extension CycleViewModel where Action == Mutation {
  public func mutate(_ action: Action) -> Mutation {
    return action
  }
}

extension CycleViewModel where Activity == NoActivity {
  public var activity: Observable<Activity> {
    preconditionFailure("\(self) does not have Activity")
  }
}

extension CycleViewModel where State == NoState {
  public var state: StateVariable<State> {
    preconditionFailure("\(self) does not have State")
  }
}

extension CycleViewModel where Action == NoAction, Mutation == NoMutation {

  public func mutate(_ action: Action) -> Mutation {
    preconditionFailure("\(self) does not have Action")
  }

  public func reduce(_ mutation: Mutation) {
    preconditionFailure("\(self) does not have Mutation")
  }
}

extension CycleViewModel {

  // TODO: Use Binder on RxSwift 4.0
  public var action: AnyObserver<Action> {
    return AnyObserver<Action> { [weak self] e in
      guard let `self` = self else { return }
      if case .next(let action) = e {
        self.reduce(self.mutate(action))
      }
    }
  }

  public func run(_ action: Action) {
    self.reduce(self.mutate(action))
  }
}

public struct CycleEvent<PropertyType> : ObservableType {

  public typealias E = PropertyType

  let _events: Observable<PropertyType>

  public init<Ev: ObservableType>(_ events: Ev) where Ev.E == E {
    _events = events.asObservable()
  }

  public func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
    return _events.subscribe(observer)
  }

  /// - returns: `Observable` interface.
  public func asObservable() -> Observable<E> {
    return _events
  }

  public func asDriver() -> Driver<E> {
    return _events.asDriver { _ in
      return .empty()
    }
  }
}

public final class StateVariable<T> {

  public var value: T {
    return source.value
  }

  private let source: MutableStateVariable<T>

  public init(_ variable: MutableStateVariable<T>) {
    self.source = variable
  }

  public func asObservable() -> Observable<T> {
    return source.asObservable()
  }

  public func asDriver() -> Driver<T> {
    return source.asDriver()
  }
}

public final class MutableStateVariable<T> {

  public var value: T {
    get {
      return source.value
    }
    set {
      source.value = newValue
    }
  }

  private let source: Variable<T>

  public init(_ value: T) {
    self.source = .init(value)
  }

  public func mutate<Source: ObservableType>(_ execute: @escaping (inout T, Source.E) -> Void) -> (Source) -> Disposable {
    return { source in
      source
        .do(onNext: { [weak self] e in
          guard let `self` = self else { return }
          execute(&self.value, e)
        })
        .subscribe()
    }
  }

  public func asObservable() -> Observable<T> {
    return source.asObservable()
  }

  public func asDriver() -> Driver<T> {
    return source.asDriver()
  }
}