struct State { var currentText: String var subviewStates: IdentifiedArray<SubviewState> var backendResponse: Response } enum Action { case buttonTapped case textUpdated(String) case subviewAction(id: Subview.ID, action: SubviewAction) } struct Environment { let dependency: Dependency let subviewEnvironment: SubviewEnvironment }
let reducer = Reducer<State, Action, Environment>() { state, action, environment in switch action { case .textUpdated(let newText): state.currentText = newText case .buttonTapped: return environment.dependency.someBackgroundWork() case .backgroundWorkFinished(let result): state.backendResponse = result case .subviewAction(let id, .someSubviewAction): // Do spmething with subview break } }
struct MyView: View { let store: Store<State, Action> var body: some View { WithViewStore(self.store) { viewStore in Button(viewStore.text) { viewStore.send(.buttonTapped) } } } }
let reducer = Reducer<State, Action, Environment>.combine( Reducer { state, action, environment in switch action { case .textUpdated(let newText): state.currentText = newText // ... }, subviewReducer.pullback( state: \.subviewState, action: /ViewAction.subviewAction, environment: \.subviewEnvironment ) })
TestStore.assert to run through an event flow of your viewTestScheduler to advance queues and test asynchronous codetestStore.assert( .send(.buttonTapped) { $0.isEditing = true }, .receive(.updateContent), .do { testQueue.advance() }, .receive(.contentReceivedFromAPI(let newContent)) { $0.content = newContent $0.description = "up to date" } )