Skip to content

Fix bad buildFinalResult warning on Swift 5.7 #1467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 93 additions & 69 deletions Sources/ComposableArchitecture/Reducer/Reducers/ForEachReducer.swift
Original file line number Diff line number Diff line change
@@ -1,73 +1,97 @@
extension ReducerProtocol {
/// Embeds a child reducer in a parent domain that works on elements of a collection in parent
/// state.
///
/// For example, if a parent feature holds onto an array of child states, then it can
/// perform its core logic _and_ the child's logic by using the `forEach` operator:
///
/// ```swift
/// struct Parent: ReducerProtocol {
/// struct State {
/// var rows: IdentifiedArrayOf<Row.State>
/// // ...
/// }
/// enum Action {
/// case row(id: Row.State.ID, action: Row.Action)
/// // ...
/// }
///
/// var body: some ReducerProtocol<State, Action> {
/// Reduce { state, action in
/// // Core logic for parent feature
/// }
/// .forEach(\.rows, action: /Action.row) {
/// Row()
/// }
/// }
/// }
/// ```
///
/// > Tip: We are using `IdentifiedArray` from our
/// [Identified Collections][swift-identified-collections] library because it provides a safe and
/// ergonomic API for accessing elements from a stable ID rather than positional indices.
///
/// The `forEach` forces a specific order of operations for the child and parent features. It runs
/// the child first, and then the parent. If the order was reversed, then it would be possible for
/// the parent feature to remove the child state from the array, in which case the child feature
/// would not be able to react to that action. That can cause subtle bugs.
///
/// It is still possible for a parent feature higher up in the application to remove the child
/// state from the array before the child has a chance to react to the action. In such cases a
/// runtime warning is shown in Xcode to let you know that there's a potential problem.
///
/// [swift-identified-collections]: http://github.com/pointfreeco/swift-identified-collections
///
/// - Parameters:
/// - toElementsState: A writable key path from parent state to an `IdentifiedArray` of child
/// state.
/// - toElementAction: A case path from parent action to child identifier and child actions.
/// - element: A reducer that will be invoked with child actions against elements of child
/// state.
/// - Returns: A reducer that combines the child reducer with the parent reducer.
@inlinable
public func forEach<ID: Hashable, Element: ReducerProtocol>(
_ toElementsState: WritableKeyPath<State, IdentifiedArray<ID, Element.State>>,
action toElementAction: CasePath<Action, (ID, Element.Action)>,
@ReducerBuilderOf<Element> _ element: () -> Element,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> _ForEachReducer<Self, ID, Element> {
_ForEachReducer(
parent: self,
toElementsState: toElementsState,
toElementAction: toElementAction,
element: element(),
file: file,
fileID: fileID,
line: line
)
}
#if swift(>=5.7)
/// Embeds a child reducer in a parent domain that works on elements of a collection in parent
/// state.
///
/// For example, if a parent feature holds onto an array of child states, then it can perform
/// its core logic _and_ the child's logic by using the `forEach` operator:
///
/// ```swift
/// struct Parent: ReducerProtocol {
/// struct State {
/// var rows: IdentifiedArrayOf<Row.State>
/// // ...
/// }
/// enum Action {
/// case row(id: Row.State.ID, action: Row.Action)
/// // ...
/// }
///
/// var body: some ReducerProtocol<State, Action> {
/// Reduce { state, action in
/// // Core logic for parent feature
/// }
/// .forEach(\.rows, action: /Action.row) {
/// Row()
/// }
/// }
/// }
/// ```
///
/// > Tip: We are using `IdentifiedArray` from our
/// [Identified Collections][swift-identified-collections] library because it provides a safe
/// and ergonomic API for accessing elements from a stable ID rather than positional indices.
///
/// The `forEach` forces a specific order of operations for the child and parent features. It
/// runs the child first, and then the parent. If the order was reversed, then it would be
/// possible for the parent feature to remove the child state from the array, in which case the
/// child feature would not be able to react to that action. That can cause subtle bugs.
///
/// It is still possible for a parent feature higher up in the application to remove the child
/// state from the array before the child has a chance to react to the action. In such cases a
/// runtime warning is shown in Xcode to let you know that there's a potential problem.
///
/// [swift-identified-collections]: http://github.com/pointfreeco/swift-identified-collections
///
/// - Parameters:
/// - toElementsState: A writable key path from parent state to an `IdentifiedArray` of child
/// state.
/// - toElementAction: A case path from parent action to child identifier and child actions.
/// - element: A reducer that will be invoked with child actions against elements of child
/// state.
/// - Returns: A reducer that combines the child reducer with the parent reducer.
@inlinable
public func forEach<ID: Hashable, ElementState, ElementAction>(
_ toElementsState: WritableKeyPath<State, IdentifiedArray<ID, ElementState>>,
action toElementAction: CasePath<Action, (ID, ElementAction)>,
@ReducerBuilder<ElementState, ElementAction> _ element: () -> some ReducerProtocol<
ElementState, ElementAction
>,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> some ReducerProtocol<State, Action> {
_ForEachReducer(
parent: self,
toElementsState: toElementsState,
toElementAction: toElementAction,
element: element(),
file: file,
fileID: fileID,
line: line
)
}
#else
@inlinable
public func forEach<ID: Hashable, Element: ReducerProtocol>(
_ toElementsState: WritableKeyPath<State, IdentifiedArray<ID, Element.State>>,
action toElementAction: CasePath<Action, (ID, Element.Action)>,
@ReducerBuilderOf<Element> _ element: () -> Element,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> _ForEachReducer<Self, ID, Element> {
_ForEachReducer(
parent: self,
toElementsState: toElementsState,
toElementAction: toElementAction,
element: element(),
file: file,
fileID: fileID,
line: line
)
}
#endif
}

public struct _ForEachReducer<
Expand Down
152 changes: 87 additions & 65 deletions Sources/ComposableArchitecture/Reducer/Reducers/IfCaseLetReducer.swift
Original file line number Diff line number Diff line change
@@ -1,69 +1,91 @@
extension ReducerProtocol {
/// Embeds a child reducer in a parent domain that works on a case of parent enum state.
///
/// For example, if a parent feature's state is expressed as an enum of multiple children states,
/// then `ifCaseLet` can run a child reducer on a particular case of the enum:
///
/// ```swift
/// struct Parent: ReducerProtocol {
/// enum State {
/// case loggedIn(Authenticated.State)
/// case loggedOut(Unauthenticated.State)
/// }
/// enum Action {
/// case loggedIn(Authenticated.Action)
/// case loggedOut(Unauthenticated.Action)
/// // ...
/// }
///
/// var body: some ReducerProtocol<State, Action> {
/// Reduce { state, action in
/// // Core logic for parent feature
/// }
/// .ifCaseLet(/State.loggedIn, action: /Action.loggedIn) {
/// Authenticated()
/// }
/// .ifCaseLet(/State.loggedOut, action: /Action.loggedOut) {
/// Unauthenticated()
/// }
/// }
/// }
/// ```
///
/// The `ifCaseLet` forces a specific order of operations for the child and parent features. It
/// runs the child first, and then the parent. If the order was reversed, then it would be
/// possible for the parent feature to change the case of the enum, in which case the child
/// feature would not be able to react to that action. That can cause subtle bugs.
///
/// It is still possible for a parent feature higher up in the application to change the case of
/// the enum before the child has a chance to react to the action. In such cases a runtime warning
/// is shown in Xcode to let you know that there's a potential problem.
///
/// - Parameters:
/// - toCaseState: A case path from parent state to a case containing child state.
/// - toCaseAction: A case path from parent action to a case containing child actions.
/// - case: A reducer that will be invoked with child actions against child state when it is
/// present
/// - Returns: A reducer that combines the child reducer with the parent reducer.
@inlinable
public func ifCaseLet<Case: ReducerProtocol>(
_ toCaseState: CasePath<State, Case.State>,
action toCaseAction: CasePath<Action, Case.Action>,
@ReducerBuilderOf<Case> then case: () -> Case,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> _IfCaseLetReducer<Self, Case> {
.init(
parent: self,
child: `case`(),
toChildState: toCaseState,
toChildAction: toCaseAction,
file: file,
fileID: fileID,
line: line
)
}
#if swift(>=5.7)
/// Embeds a child reducer in a parent domain that works on a case of parent enum state.
///
/// For example, if a parent feature's state is expressed as an enum of multiple children
/// states, then `ifCaseLet` can run a child reducer on a particular case of the enum:
///
/// ```swift
/// struct Parent: ReducerProtocol {
/// enum State {
/// case loggedIn(Authenticated.State)
/// case loggedOut(Unauthenticated.State)
/// }
/// enum Action {
/// case loggedIn(Authenticated.Action)
/// case loggedOut(Unauthenticated.Action)
/// // ...
/// }
///
/// var body: some ReducerProtocol<State, Action> {
/// Reduce { state, action in
/// // Core logic for parent feature
/// }
/// .ifCaseLet(/State.loggedIn, action: /Action.loggedIn) {
/// Authenticated()
/// }
/// .ifCaseLet(/State.loggedOut, action: /Action.loggedOut) {
/// Unauthenticated()
/// }
/// }
/// }
/// ```
///
/// The `ifCaseLet` forces a specific order of operations for the child and parent features. It
/// runs the child first, and then the parent. If the order was reversed, then it would be
/// possible for the parent feature to change the case of the enum, in which case the child
/// feature would not be able to react to that action. That can cause subtle bugs.
///
/// It is still possible for a parent feature higher up in the application to change the case of
/// the enum before the child has a chance to react to the action. In such cases a runtime
/// warning is shown in Xcode to let you know that there's a potential problem.
///
/// - Parameters:
/// - toCaseState: A case path from parent state to a case containing child state.
/// - toCaseAction: A case path from parent action to a case containing child actions.
/// - case: A reducer that will be invoked with child actions against child state when it is
/// present
/// - Returns: A reducer that combines the child reducer with the parent reducer.
@inlinable
public func ifCaseLet<CaseState, CaseAction>(
_ toCaseState: CasePath<State, CaseState>,
action toCaseAction: CasePath<Action, CaseAction>,
@ReducerBuilder<CaseState, CaseAction> then case: () -> some ReducerProtocol<CaseState, CaseAction>,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> some ReducerProtocol<State, Action> {
_IfCaseLetReducer(
parent: self,
child: `case`(),
toChildState: toCaseState,
toChildAction: toCaseAction,
file: file,
fileID: fileID,
line: line
)
}
#else
@inlinable
public func ifCaseLet<Case: ReducerProtocol>(
_ toCaseState: CasePath<State, Case.State>,
action toCaseAction: CasePath<Action, Case.Action>,
@ReducerBuilderOf<Case> then case: () -> Case,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) -> _IfCaseLetReducer<Self, Case> {
.init(
parent: self,
child: `case`(),
toChildState: toCaseState,
toChildAction: toCaseAction,
file: file,
fileID: fileID,
line: line
)
}
#endif
}

public struct _IfCaseLetReducer<Parent: ReducerProtocol, Child: ReducerProtocol>: ReducerProtocol {
Expand Down
Loading