Skip to content
Snippets Groups Projects
AppFeature.swift 2.56 KiB
Newer Older
import Combine
import ComposableArchitecture
import ComposablePresentation
import LandingFeature
import SessionFeature
Dariusz Rybicki's avatar
Dariusz Rybicki committed
import XCTestDynamicOverlay

struct AppState: Equatable {
  enum Scene: Equatable {
    case landing(LandingState)
    case session(SessionState)
  }

  var id: UUID = UUID()
  var scene: Scene = .landing(LandingState(id: UUID()))
}

extension AppState.Scene {
  var asLanding: LandingState? {
    get {
      guard case .landing(let state) = self else { return nil }
      return state
    }
    set {
      guard let newValue = newValue else { return }
      self = .landing(newValue)
    }
  }

  var asSession: SessionState? {
    get {
      guard case .session(let state) = self else { return nil }
      return state
    }
    set {
      guard let newValue = newValue else { return }
      self = .session(newValue)
    }
  }
}

enum AppAction: Equatable {
  case viewDidLoad
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  case cMixDidChange(hasCMix: Bool)
  case landing(LandingAction)
  case session(SessionAction)
}

struct AppEnvironment {
  var makeId: () -> UUID
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  var hasCMix: () -> AnyPublisher<Bool, Never>
  var mainScheduler: AnySchedulerOf<DispatchQueue>
  var landing: LandingEnvironment
  var session: SessionEnvironment
}

let appReducer = Reducer<AppState, AppAction, AppEnvironment>
{ state, action, env in
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  enum HasCMixEffectId {}
  switch action {
  case .viewDidLoad:
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    return env.hasCMix()
      .removeDuplicates()
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      .map(AppAction.cMixDidChange(hasCMix:))
      .receive(on: env.mainScheduler)
      .eraseToEffect()
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      .cancellable(id: HasCMixEffectId.self, cancelInFlight: true)
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  case .cMixDidChange(let hasClient):
    if hasClient {
      let sessionState = state.scene.asSession ?? SessionState(id: env.makeId())
      state.scene = .session(sessionState)
    } else {
      let landingState = state.scene.asLanding ?? LandingState(id: env.makeId())
      state.scene = .landing(landingState)
    }
    return .none

  case .landing(_), .session(_):
    return .none
  }
}
.presenting(
  landingReducer,
  state: .keyPath(\.scene.asLanding),
  id: .notNil(),
  action: /AppAction.landing,
  environment: \.landing
)
.presenting(
  sessionReducer,
  state: .keyPath(\.scene.asSession),
  id: .notNil(),
  action: /AppAction.session,
  environment: \.session
)

extension AppEnvironment {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  static let unimplemented = AppEnvironment(
    makeId: XCTUnimplemented("\(Self.self).makeId", placeholder: UUID()),
    hasCMix: XCTUnimplemented("\(Self.self).hasCMix", placeholder: Empty().eraseToAnyPublisher()),
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    mainScheduler: .unimplemented,
    landing: .unimplemented,
    session: .unimplemented