diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index b87e344a2e43..148b94ec1204 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "ace21305e0dd3a9e749aef79fef14be79a3b4669", - "version" : "0.8.2" + "revision" : "62041e6016a30f56952f5d7d3f12a3fd7029e1cd", + "version" : "0.8.3" } } ], diff --git a/Sources/ComposableArchitecture/Documentation.docc/Articles/MigratingToTheReducerProtocol.md b/Sources/ComposableArchitecture/Documentation.docc/Articles/MigratingToTheReducerProtocol.md index 320f08a7c073..f1b40bf88c32 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Articles/MigratingToTheReducerProtocol.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Articles/MigratingToTheReducerProtocol.md @@ -417,7 +417,7 @@ Similar to `optional` reducers, another common pattern in applications is the us ``AnyReducer/forEach(state:action:environment:file:fileID:line:)-2ypoa`` to allow running a reducer on each element of a collection. Converting such child and parent reducers will look nearly identical to what we did above for optional reducers, but it will make use of the new -``ReducerProtocol/forEach(_:action:_:file:fileID:line:)`` operator instead. +``ReducerProtocol/forEach(_:action:element:file:fileID:line:)`` operator instead. In particular, the new `forEach` method operates on the parent reducer by specifying the collection sub-state you want to work on, and providing the element reducer you want to be able to run on diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReduceDeprecations.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReduceDeprecations.md new file mode 100644 index 000000000000..6b8aac153777 --- /dev/null +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReduceDeprecations.md @@ -0,0 +1,9 @@ +# Deprecations + +Review unsupported `Reduce` APIs. + +## Topics + +### Reducer structure + +- ``Reduce/init(_:environment:)`` diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReducerDeprecations.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReducerDeprecations.md index a1f92b7aaa5e..9e39aad797b7 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReducerDeprecations.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ReducerDeprecations.md @@ -14,4 +14,3 @@ instead. - ``AnyReducer`` - ``Reducer`` - ``DebugEnvironment`` -- ``Identified`` diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/SwiftUIDeprecations.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/SwiftUIDeprecations.md index 58d84ee26856..c0e5651cba04 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/SwiftUIDeprecations.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/SwiftUIDeprecations.md @@ -15,23 +15,5 @@ Avoid using deprecated APIs in your app. Select a method to see the replacement ### WithViewStore -- ``WithViewStore/init(_:content:file:line:)-1gjbi`` -- ``WithViewStore/init(_:content:file:line:)-2uj44`` -- ``WithViewStore/init(_:content:file:line:)-5vj3w`` -- ``WithViewStore/init(_:content:file:line:)-5zsmz`` -- ``WithViewStore/init(_:content:file:line:)-7kai`` -- ``WithViewStore/init(_:file:line:content:)-4xog0`` -- ``WithViewStore/init(_:file:line:content:)-55smh`` -- ``WithViewStore/init(_:file:line:content:)-7qkc1`` -- ``WithViewStore/init(_:file:line:content:)-8b21b`` -- ``WithViewStore/init(_:file:line:content:)-9b6e2`` -- ``WithViewStore/init(_:removeDuplicates:content:file:line:)-1lyhl`` -- ``WithViewStore/init(_:removeDuplicates:content:file:line:)-35xje`` -- ``WithViewStore/init(_:removeDuplicates:content:file:line:)-8zzun`` -- ``WithViewStore/init(_:removeDuplicates:content:file:line:)-9atby`` -- ``WithViewStore/init(_:removeDuplicates:file:line:content:)`` - ``WithViewStore/Action`` - ``WithViewStore/State`` - - - diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/TestStoreDeprecations.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/TestStoreDeprecations.md index 92ee48d2b651..f7609b4069e9 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/TestStoreDeprecations.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/TestStoreDeprecations.md @@ -11,6 +11,7 @@ Avoid using deprecated APIs in your app. Select a method to see the replacement ### Creating a test store - ``TestStore/init(initialState:reducer:environment:file:line:)`` +- ``TestStore/init(initialState:reducer:prepareDependencies:file:line:)-72tkt`` ### Configuring a test store @@ -20,8 +21,8 @@ Avoid using deprecated APIs in your app. Select a method to see the replacement - ``TestStore/send(_:assert:file:line:)-30pjj`` - ``TestStore/receive(_:assert:file:line:)-2nhm0`` -- ``TestStore/receive(_:assert:file:line:)-6fuav`` -- ``TestStore/receive(_:assert:file:line:)-u5tf`` +- ``TestStore/receive(_:assert:file:line:)-1bfw4`` +- ``TestStore/receive(_:assert:file:line:)-5o4u3`` - ``TestStore/assert(_:file:line:)-707lb`` - ``TestStore/assert(_:file:line:)-4gff7`` - ``TestStore/LocalState`` @@ -32,3 +33,8 @@ Avoid using deprecated APIs in your app. Select a method to see the replacement - ``TestStore/skipReceivedActions(strict:file:line:)-3nldt`` - ``TestStore/skipInFlightEffects(strict:file:line:)-95n5f`` + +### Scoping test stores + +- ``TestStore/scope(state:action:)`` +- ``TestStore/scope(state:)`` diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ViewStoreDeprecations.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ViewStoreDeprecations.md index fc1fcffb7e29..35789a9f5923 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ViewStoreDeprecations.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/ViewStoreDeprecations.md @@ -19,5 +19,5 @@ Avoid using deprecated APIs in your app. Select a method to see the replacement ### SwiftUI integration -- ``ViewStore/subscript(dynamicMember:)-7xjrv`` +- ``ViewStore/subscript(dynamicMember:)-3q4xh`` - ``ViewStore/binding(keyPath:send:)`` diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Effect.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Effect.md index 24dbc3e27df5..38a739ea3afa 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Effect.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Effect.md @@ -24,10 +24,6 @@ - ``EffectPublisher/merge(_:)-45guh`` - ``EffectPublisher/merge(_:)-3d54p`` -### Concurrency - -- ``UncheckedSendable`` - ### Testing - ``EffectPublisher/unimplemented(_:)`` diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Reduce.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Reduce.md new file mode 100644 index 000000000000..1578ca0de59d --- /dev/null +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Reduce.md @@ -0,0 +1,15 @@ +# ``ComposableArchitecture/Reduce`` + +## Topics + +### Creating a reducer + +- ``init(_:)-17fld`` + +### Type erased reducers + +- ``init(_:)-3rph8`` + +### Deprecations + +- diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/ReducerProtocol.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/ReducerProtocol.md index 6f50614321c7..f13430bab30e 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/ReducerProtocol.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/ReducerProtocol.md @@ -7,7 +7,7 @@ - ``reduce(into:action:)-8yinq`` - ``State`` - ``Action`` -- ``EffectPublisher`` +- ``EffectTask`` ### Reducer composition @@ -17,21 +17,21 @@ - ``Scope`` - ``ifLet(_:action:then:file:fileID:line:)`` - ``ifCaseLet(_:action:then:file:fileID:line:)`` -- ``forEach(_:action:_:file:fileID:line:)`` +- ``forEach(_:action:element:file:fileID:line:)`` ### Supporting reducers -- ``BindingReducer`` +- ``Reduce`` - ``CombineReducers`` - ``EmptyReducer`` -- ``Reduce`` +- ``BindingReducer`` ### Reducer modifiers - ``dependency(_:_:)`` - ``transformDependency(_:transform:)`` +- ``_printChanges(_:)`` - ``signpost(_:log:)`` -- ``debug()`` ### Deprecations diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Store.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Store.md index 708a5e352027..9885a919f617 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/Store.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/Store.md @@ -4,7 +4,7 @@ ### Creating a store -- ``init(initialState:reducer:)`` +- ``init(initialState:reducer:prepareDependencies:)`` - ``StoreOf`` ### Scoping stores diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/TaskResult.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/TaskResult.md new file mode 100644 index 000000000000..809a12eb866e --- /dev/null +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/TaskResult.md @@ -0,0 +1,22 @@ +# ``ComposableArchitecture/TaskResult`` + +## Topics + +### Representing a task result + +- ``success(_:)`` +- ``failure(_:)`` + +### Converting a throwing expression + +- ``init(catching:)`` + +### Accessing a result's value + +- ``value`` + +### Transforming results + +- ``map(_:)`` +- ``flatMap(_:)`` +- ``init(_:)`` diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md index f255cf3f4c46..7df8972f8578 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md @@ -4,7 +4,9 @@ ### Creating a test store -- ``init(initialState:reducer:prepareDependencies:file:line:)`` +- ``init(initialState:reducer:prepareDependencies:file:line:)-55zkv`` +- ``init(initialState:reducer:observe:prepareDependencies:file:line:)`` +- ``init(initialState:reducer:observe:send:prepareDependencies:file:line:)`` ### Configuring a test store @@ -16,8 +18,8 @@ - ``send(_:assert:file:line:)-1ax61`` - ``receive(_:timeout:assert:file:line:)-1rwdd`` -- ``receive(_:timeout:assert:file:line:)-4e4m0`` -- ``receive(_:timeout:assert:file:line:)-3myco`` +- ``receive(_:timeout:assert:file:line:)-8xkqt`` +- ``receive(_:timeout:assert:file:line:)-2ju31`` - ``finish(timeout:file:line:)`` - ``TestStoreTask`` @@ -32,11 +34,6 @@ While the most common way of interacting with a test store's state is via its `` - ``state`` -### Scoping test stores - -- ``scope(state:action:)`` -- ``scope(state:)`` - ### Deprecations - diff --git a/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducer.swift b/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducer.swift index e090ed21ecbe..c8bd529d5ddb 100644 --- a/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducer.swift +++ b/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducer.swift @@ -451,7 +451,7 @@ public struct AnyReducer { /// This API has been soft-deprecated in favor of /// ``ReducerProtocol/ifCaseLet(_:action:then:file:fileID:line:)`` and - /// ``Scope/init(state:action:_:file:fileID:line:)``. Read + /// ``Scope/init(state:action:child:file:fileID:line:)``. Read /// for more information. /// /// Transforms a reducer that works on child state, action, and environment into one that works on @@ -942,7 +942,7 @@ public struct AnyReducer { } /// This API has been soft-deprecated in favor of - /// ``ReducerProtocol/forEach(_:action:_:file:fileID:line:)``. Read + /// ``ReducerProtocol/forEach(_:action:element:file:fileID:line:)``. Read /// for more information. /// /// A version of ``pullback(state:action:environment:)`` that transforms a reducer that works on diff --git a/Sources/ComposableArchitecture/Reducer/Reducers/DebugReducer.swift b/Sources/ComposableArchitecture/Reducer/Reducers/DebugReducer.swift index 2273289e92c3..71e7eb959b54 100644 --- a/Sources/ComposableArchitecture/Reducer/Reducers/DebugReducer.swift +++ b/Sources/ComposableArchitecture/Reducer/Reducers/DebugReducer.swift @@ -6,6 +6,9 @@ extension ReducerProtocol { /// /// - Parameter printer: A printer for printing debug messages. /// - Returns: A reducer that prints debug messages for all received actions. + #if swift(>=5.8) + @_documentation(visibility: public) + #endif @inlinable public func _printChanges( _ printer: _ReducerPrinter? = .customDump diff --git a/Sources/ComposableArchitecture/Reducer/Reducers/Scope.swift b/Sources/ComposableArchitecture/Reducer/Reducers/Scope.swift index 1c227983b15f..0783199302d7 100644 --- a/Sources/ComposableArchitecture/Reducer/Reducers/Scope.swift +++ b/Sources/ComposableArchitecture/Reducer/Reducers/Scope.swift @@ -29,7 +29,7 @@ /// ``` /// /// A parent reducer with a domain that holds onto the child domain can use -/// ``init(state:action:_:)`` to embed the child reducer in its +/// ``init(state:action:child:)`` to embed the child reducer in its /// ``ReducerProtocol/body-swift.property-7foai``: /// /// ```swift @@ -58,7 +58,7 @@ /// ## Enum state /// /// The ``Scope`` reducer also works when state is modeled as an enum, not just a struct. In that -/// case you can use ``init(state:action:_:file:fileID:line:)`` to specify a case path that +/// case you can use ``init(state:action:child:file:fileID:line:)`` to specify a case path that /// identifies the case of state you want to scope to. /// /// For example, if your state was modeled as an enum for unloaded/loading/loaded, you could @@ -96,7 +96,8 @@ /// For an alternative to using ``Scope`` with state case paths that enforces the order, check out /// the ``ifCaseLet(_:action:then:file:fileID:line:)`` operator. public struct Scope: ReducerProtocol { - public enum StatePath { + @usableFromInline + enum StatePath { case casePath( CasePath, file: StaticString, @@ -106,9 +107,14 @@ public struct Scope: ReducerP case keyPath(WritableKeyPath) } - public let toChildState: StatePath - public let toChildAction: CasePath - public let child: Child + @usableFromInline + let toChildState: StatePath + + @usableFromInline + let toChildAction: CasePath + + @usableFromInline + let child: Child @usableFromInline init( diff --git a/Sources/ComposableArchitecture/Store.swift b/Sources/ComposableArchitecture/Store.swift index b75942f3fc1e..f4b5564bc83a 100644 --- a/Sources/ComposableArchitecture/Store.swift +++ b/Sources/ComposableArchitecture/Store.swift @@ -116,10 +116,10 @@ import Foundation /// #### Thread safety checks /// /// The store performs some basic thread safety checks in order to help catch mistakes. Stores -/// constructed via the initializer ``init(initialState:reducer:)`` are assumed to run -/// only on the main thread, and so a check is executed immediately to make sure that is the case. -/// Further, all actions sent to the store and all scopes (see ``scope(state:action:)``) of the -/// store are also checked to make sure that work is performed on the main thread. +/// constructed via the initializer ``init(initialState:reducer:prepareDependencies:)`` are assumed +/// to run only on the main thread, and so a check is executed immediately to make sure that is the +/// case. Further, all actions sent to the store and all scopes (see ``scope(state:action:)``) of +/// the store are also checked to make sure that work is performed on the main thread. public final class Store { private var bufferedActions: [Action] = [] @_spi(Internals) public var effectCancellables: [UUID: AnyCancellable] = [:] diff --git a/Sources/ComposableArchitecture/SwiftUI/ForEachStore.swift b/Sources/ComposableArchitecture/SwiftUI/ForEachStore.swift index 61a68aaa6d6f..bf1e821f57dd 100644 --- a/Sources/ComposableArchitecture/SwiftUI/ForEachStore.swift +++ b/Sources/ComposableArchitecture/SwiftUI/ForEachStore.swift @@ -54,7 +54,7 @@ import SwiftUI /// } /// ``` /// -/// Enhance its core reducer using ``ReducerProtocol/forEach(_:action:_:file:fileID:line:)``: +/// Enhance its core reducer using ``ReducerProtocol/forEach(_:action:element:file:fileID:line:)``: /// /// ```swift /// var body: some ReducerProtocol { diff --git a/Sources/ComposableArchitecture/SwiftUI/SwitchStore.swift b/Sources/ComposableArchitecture/SwiftUI/SwitchStore.swift index dec5abfd66b8..89e94f003f50 100644 --- a/Sources/ComposableArchitecture/SwiftUI/SwitchStore.swift +++ b/Sources/ComposableArchitecture/SwiftUI/SwitchStore.swift @@ -51,7 +51,7 @@ import SwiftUI /// ``` /// /// See ``ReducerProtocol/ifCaseLet(_:action:then:file:fileID:line:)`` and -/// ``Scope/init(state:action:_:file:fileID:line:)`` for embedding reducers that operate on each +/// ``Scope/init(state:action:child:file:fileID:line:)`` for embedding reducers that operate on each /// case of an enum in reducers that operate on the entire enum. public struct SwitchStore: View { public let store: Store diff --git a/Sources/ComposableArchitecture/TestStore.swift b/Sources/ComposableArchitecture/TestStore.swift index 5c76fbac51f9..a1c622c7b6aa 100644 --- a/Sources/ComposableArchitecture/TestStore.swift +++ b/Sources/ComposableArchitecture/TestStore.swift @@ -1043,7 +1043,9 @@ extension TestStore where ScopedState: Equatable { let previousState = self.reducer.state let task = self.store .send(.init(origin: .send(self.fromScopedAction(action)), file: file, line: line)) - await self.reducer.effectDidSubscribe.stream.first(where: { _ in true }) + for await _ in self.reducer.effectDidSubscribe.stream { + break + } do { let currentState = self.state self.reducer.state = previousState @@ -1454,7 +1456,7 @@ extension TestStore where ScopedState: Equatable, Action: Equatable { extension TestStore where ScopedState: Equatable { /// Asserts a matching action was received from an effect and asserts how the state changes. /// - /// See ``receive(_:timeout:assert:file:line:)-3myco`` for more information of how to use this + /// See ``receive(_:timeout:assert:file:line:)-2ju31`` for more information of how to use this /// method. /// /// - Parameters: @@ -1491,7 +1493,7 @@ extension TestStore where ScopedState: Equatable { /// Asserts an action was received matching a case path and asserts how the state changes. /// - /// See ``receive(_:timeout:assert:file:line:)-4e4m0`` for more information of how to use this + /// See ``receive(_:timeout:assert:file:line:)-8xkqt`` for more information of how to use this /// method. /// /// - Parameters: @@ -1601,7 +1603,7 @@ extension TestStore where ScopedState: Equatable { /// was in the effect that you chose not to assert on. /// /// If you only want to check that a particular action case was received, then you might find the - /// ``receive(_:timeout:assert:file:line:)-4e4m0`` overload of this method more useful. + /// ``receive(_:timeout:assert:file:line:)-8xkqt`` overload of this method more useful. /// /// - Parameters: /// - isMatching: A closure that attempts to match an action. If it returns `false`, a test @@ -2402,7 +2404,9 @@ public enum Exhaustivity: Equatable { /// ``TestStore/skipInFlightEffects(strict:file:line:)-5hbsk``. /// /// To partially match an action received from an effect, use - /// ``TestStore/receive(_:timeout:assert:file:line:)-4e4m0``. + /// ``TestStore/receive(_:timeout:assert:file:line:)-8xkqt`` or + /// ``TestStore/receive(_:timeout:assert:file:line:)-2ju31``. + case on /// Non-exhaustive assertions.