diff --git a/Examples/Integration/Integration/Legacy/LegacyPresentationTestCase.swift b/Examples/Integration/Integration/Legacy/LegacyPresentationTestCase.swift index 35ed3323f229..8f6db19c39a0 100644 --- a/Examples/Integration/Integration/Legacy/LegacyPresentationTestCase.swift +++ b/Examples/Integration/Integration/Legacy/LegacyPresentationTestCase.swift @@ -2,7 +2,7 @@ import SwiftUI import SwiftUINavigation -fileprivate enum PresentationTestCase { +private enum PresentationTestCase { @Reducer struct Feature { struct State: Equatable { @@ -86,8 +86,8 @@ fileprivate enum PresentationTestCase { return .none case .destination(.presented(.fullScreenCover(.parentSendDismissActionButtonTapped))), - .destination(.presented(.sheet(.parentSendDismissActionButtonTapped))), - .destination(.presented(.popover(.parentSendDismissActionButtonTapped))): + .destination(.presented(.sheet(.parentSendDismissActionButtonTapped))), + .destination(.presented(.popover(.parentSendDismissActionButtonTapped))): return .send(.destination(.dismiss)) case let .destination(.presented(.alert(alertAction))): @@ -141,9 +141,9 @@ fileprivate enum PresentationTestCase { } case .destination(.presented(.fullScreenCover(.dismissAndAlert))), - .destination(.presented(.popover(.dismissAndAlert))), - .destination(.presented(.navigationDestination(.dismissAndAlert))), - .destination(.presented(.sheet(.dismissAndAlert))): + .destination(.presented(.popover(.dismissAndAlert))), + .destination(.presented(.navigationDestination(.dismissAndAlert))), + .destination(.presented(.sheet(.dismissAndAlert))): state.destination = .alert( AlertState { TextState("Hello!") diff --git a/Examples/Integration/Integration/Legacy/PresentationItemTestCase.swift b/Examples/Integration/Integration/Legacy/PresentationItemTestCase.swift index a8422eb1c123..c80cbdeaa9af 100644 --- a/Examples/Integration/Integration/Legacy/PresentationItemTestCase.swift +++ b/Examples/Integration/Integration/Legacy/PresentationItemTestCase.swift @@ -1,7 +1,7 @@ import ComposableArchitecture import SwiftUI -fileprivate enum PresentationItemTestCase { +private enum PresentationItemTestCase { @Reducer struct Feature { @Reducer diff --git a/Sources/ComposableArchitecture/CaseReducer.swift b/Sources/ComposableArchitecture/CaseReducer.swift index d003fe07c91a..d4f299da5c0f 100644 --- a/Sources/ComposableArchitecture/CaseReducer.swift +++ b/Sources/ComposableArchitecture/CaseReducer.swift @@ -2,7 +2,7 @@ /// /// You should not conform to this protocol directly. Instead, the ``Reducer()`` macro will add a /// conformance to enums. -public protocol CaseReducer: Reducer +public protocol CaseReducer: Reducer where State: CaseReducerState, Body: Reducer, Body.State == State, Body.Action == Action { associatedtype State = State associatedtype Action = Action diff --git a/Sources/ComposableArchitecture/Macros.swift b/Sources/ComposableArchitecture/Macros.swift index e306bca7c41e..8536598338bb 100644 --- a/Sources/ComposableArchitecture/Macros.swift +++ b/Sources/ComposableArchitecture/Macros.swift @@ -9,11 +9,11 @@ member, names: named(State), - named(Action), - named(init), - named(body), - named(CaseScope), - named(scope) + named(Action), + named(init), + named(body), + named(CaseScope), + named(scope) ) @attached(memberAttribute) @attached(extension, conformances: Reducer, CaseReducer) @@ -32,11 +32,11 @@ member, names: named(State), - named(Action), - named(init), - named(body), - named(CaseScope), - named(scope) + named(Action), + named(init), + named(body), + named(CaseScope), + named(scope) ) @attached(memberAttribute) @attached(extension, conformances: Reducer, CaseReducer) @@ -51,31 +51,31 @@ /// /// See for more /// information. - @_documentation(visibility: public) + @_documentation(visibility:public) public struct _SynthesizedConformance {} #else public struct _SynthesizedConformance {} #endif - extension _SynthesizedConformance { - /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Codable` - /// protocol. - public static let codable = Self() - /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Decodable` - /// protocol. - public static let decodable = Self() - /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Encodable` - /// protocol. - public static let encodable = Self() - /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Equatable` - /// protocol. - public static let equatable = Self() - /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Hashable` - /// protocol. - public static let hashable = Self() - /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Sendable` - /// protocol. - public static let sendable = Self() - } + extension _SynthesizedConformance { + /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Codable` + /// protocol. + public static let codable = Self() + /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Decodable` + /// protocol. + public static let decodable = Self() + /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Encodable` + /// protocol. + public static let encodable = Self() + /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Equatable` + /// protocol. + public static let equatable = Self() + /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Hashable` + /// protocol. + public static let hashable = Self() + /// Extends the `State` or `Action` types that ``Reducer()`` creates with the `Sendable` + /// protocol. + public static let sendable = Self() + } /// Marks the case of an enum reducer as holding onto "ephemeral" state. /// @@ -201,4 +201,3 @@ module: "ComposableArchitectureMacros", type: "ViewActionMacro" ) where R.Action: ViewAction #endif - diff --git a/Sources/ComposableArchitectureMacros/ReducerMacro.swift b/Sources/ComposableArchitectureMacros/ReducerMacro.swift index 00420d7c9916..7b1ef5cbf4d3 100644 --- a/Sources/ComposableArchitectureMacros/ReducerMacro.swift +++ b/Sources/ComposableArchitectureMacros/ReducerMacro.swift @@ -25,7 +25,8 @@ extension ReducerMacro: ExtensionMacro { { return [] } - let proto = declaration.isEnum + let proto = + declaration.isEnum ? "ComposableArchitecture.CaseReducer, ComposableArchitecture.Reducer" : "ComposableArchitecture.Reducer" let ext: DeclSyntax = @@ -132,7 +133,8 @@ extension ReducerMacro: MemberAttributeMacro { !attributeName.starts(with: "ComposableArchitecture.ReducerBuilder") else { return [] } } - let genericArguments = genericArguments.count == 1 + let genericArguments = + genericArguments.count == 1 ? "\(genericArguments.description).State, \(genericArguments.description).Action" : "\(genericArguments)" return [ @@ -176,8 +178,8 @@ extension ReducerMacro: MemberAttributeMacro { } } -private extension IdentifierTypeSyntax { - var isEphemeral: Bool { +extension IdentifierTypeSyntax { + fileprivate var isEphemeral: Bool { self.name.text == "AlertState" || self.name.text == "ConfirmationDialogState" } } @@ -215,20 +217,23 @@ extension ReducerMacro: MemberMacro { } return true } - let hasExplicitReducerBody = bindings.contains { - guard $0.initializer == nil - else { return true } - guard let name = $0.typeAnnotation?.type.as(SomeOrAnyTypeSyntax.self)?.constraint - .as(IdentifierTypeSyntax.self)?.name.text - else { - return false - } - return ["Reducer", "ReducerOf"].withQualified.contains(name) - } || hasReduceMethod - let hasBody = bindings.contains { - $0.as(PatternBindingSyntax.self)?.pattern - .as(IdentifierPatternSyntax.self)?.identifier.text == "body" - } || hasReduceMethod + let hasExplicitReducerBody = + bindings.contains { + guard $0.initializer == nil + else { return true } + guard + let name = $0.typeAnnotation?.type.as(SomeOrAnyTypeSyntax.self)?.constraint + .as(IdentifierTypeSyntax.self)?.name.text + else { + return false + } + return ["Reducer", "ReducerOf"].withQualified.contains(name) + } || hasReduceMethod + let hasBody = + bindings.contains { + $0.as(PatternBindingSyntax.self)?.pattern + .as(IdentifierPatternSyntax.self)?.identifier.text == "body" + } || hasReduceMethod var decls: [DeclSyntax] = [] if let enumDecl = declaration.as(EnumDeclSyntax.self) { let enumCaseElements = enumDecl.memberBlock @@ -257,7 +262,8 @@ extension ReducerMacro: MemberMacro { parameter.type.is(IdentifierTypeSyntax.self) || parameter.type.is(MemberTypeSyntax.self) { let type = parameter.type - let stateCase = enumCaseElement.attribute == .ephemeral + let stateCase = + enumCaseElement.attribute == .ephemeral ? element : element.suffixed("State") stateCaseDecls.append("case \(stateCase.trimmedDescription)") @@ -311,7 +317,8 @@ extension ReducerMacro: MemberMacro { if case let .argumentList(arguments) = node.arguments, let startIndex = arguments.firstIndex(where: { $0.label?.text == "state" }) { - let endIndex = arguments.firstIndex(where: { $0.label?.text == "action" }) + let endIndex = + arguments.firstIndex(where: { $0.label?.text == "action" }) ?? arguments.endIndex conformances.append( contentsOf: arguments[startIndex.. Self { +extension EnumCaseElementSyntax { + fileprivate func suffixed(_ suffix: TokenSyntax) -> Self { var element = self if var parameterClause = element.parameterClause, let type = parameterClause.parameters.first?.type @@ -531,8 +539,8 @@ private extension EnumCaseElementSyntax { } } -private extension AttributeListSyntax { - func contains(_ name: TokenSyntax) -> Bool { +extension AttributeListSyntax { + fileprivate func contains(_ name: TokenSyntax) -> Bool { self.contains { guard case let .attribute(attribute) = $0,