// NB: This file contains compile-time tests to ensure reducer builder generic inference is working. import ComposableArchitecture import XCTest @Reducer private struct Test { struct State {} enum Action { case tap } var body: some Reducer { EmptyReducer() } @available(iOS, introduced: 9999) @Reducer struct Unavailable { var body: some Reducer { EmptyReducer() } } } func testExistentialReducers() { _ = CombineReducers { Test() Test() as any ReducerOf } } func testLimitedAvailability() { _ = CombineReducers { Test() if #available(iOS 9999, *) { Test.Unavailable() } else if #available(iOS 8888, *) { EmptyReducer() } } } @Reducer private struct Root { struct State { var feature: Feature.State var optionalFeature: Feature.State? var enumFeature: Features.State? var features: IdentifiedArrayOf } enum Action { case feature(Feature.Action) case optionalFeature(Feature.Action) case enumFeature(Features.Action) case features(id: Feature.State.ID, action: Feature.Action) } @available(iOS, introduced: 9999) @Reducer struct Unavailable { let body = EmptyReducer() } var body: some ReducerOf { CombineReducers { Scope(state: \.feature, action: \.feature) { Feature() Feature() } Scope(state: \.feature, action: \.feature) { Feature() Feature() } } .ifLet(\.optionalFeature, action: \.optionalFeature) { Feature() Feature() } .ifLet(\.enumFeature, action: \.enumFeature) { EmptyReducer() .ifCaseLet(\.featureA, action: \.featureA) { Feature() Feature() } .ifCaseLet(\.featureB, action: \.featureB) { Feature() Feature() } Features() } .forEach(\.features, action: \.features) { Feature() Feature() } } @ReducerBuilder var testFlowControl: some ReducerOf { if true { Self() } if Bool.random() { Self() } else { EmptyReducer() } for _ in 1...10 { Self() } if #available(iOS 9999, *) { Unavailable() } } @Reducer struct Feature { struct State: Identifiable { let id: Int } enum Action { case action } var body: some Reducer { EmptyReducer() } } @Reducer struct Features { enum State { case featureA(Feature.State) case featureB(Feature.State) } enum Action { case featureA(Feature.Action) case featureB(Feature.Action) } var body: some ReducerOf { Scope(state: \.featureA, action: \.featureA) { Feature() } Scope(state: \.featureB, action: \.featureB) { Feature() } } } } @Reducer private struct IfLetExample { struct State { var optional: Int? } enum Action {} var body: some ReducerOf { EmptyReducer().ifLet(\.optional, action: \.self) { EmptyReducer() } } } @Reducer private struct IfCaseLetExample { enum State { case value(Int) } enum Action {} var body: some ReducerOf { EmptyReducer().ifCaseLet(\.value, action: \.self) { EmptyReducer() } } } @Reducer private struct ForEachExample { struct Element: Identifiable { let id: Int } struct State { var values: IdentifiedArrayOf } enum Action { case value(id: Element.ID, action: Never) } var body: some ReducerOf { EmptyReducer().forEach(\.values, action: \.value) { EmptyReducer() } } } @Reducer private struct ScopeIfLetExample { struct State { var optionalSelf: Self? { get { self } set { newValue.map { self = $0 } } } } enum Action {} var body: some ReducerOf { Scope(state: \.self, action: \.self) { EmptyReducer() .ifLet(\.optionalSelf, action: \.self) { EmptyReducer() } } } }