Skip to content

Commit 4266744

Browse files
authored
Soft-deprecate EffectPublisher (#1791)
* Soft-deprecate EffectPublisher in favor of EffectTask. * wip
1 parent 32f967c commit 4266744

File tree

6 files changed

+94
-64
lines changed

6 files changed

+94
-64
lines changed

Sources/ComposableArchitecture/Documentation.docc/ComposableArchitecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ day-to-day when building applications, such as:
6060
### State management
6161

6262
- ``ReducerProtocol``
63-
- ``EffectPublisher``
63+
- ``EffectTask``
6464
- ``Store``
6565
- ``ViewStore``
6666

Sources/ComposableArchitecture/Documentation.docc/Extensions/Deprecations/SwiftUIDeprecations.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,5 @@ Avoid using deprecated APIs in your app. Select a method to see the replacement
3333
- ``WithViewStore/Action``
3434
- ``WithViewStore/State``
3535

36-
### View state
37-
38-
- ``ActionSheetState``
39-
4036
<!--DocC: Can't currently document `View` extensions-->
4137
<!--### View Modifiers-->

Sources/ComposableArchitecture/Documentation.docc/Extensions/Effect.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
1-
# ``ComposableArchitecture/EffectPublisher``
1+
# ``ComposableArchitecture/EffectTask``
22

33
## Topics
44

55
### Creating an effect
66

7-
- ``none``
8-
- ``task(priority:operation:catch:file:fileID:line:)``
9-
- ``run(priority:operation:catch:file:fileID:line:)``
10-
- ``fireAndForget(priority:_:)``
7+
- ``EffectPublisher/none``
8+
- ``EffectPublisher/task(priority:operation:catch:file:fileID:line:)``
9+
- ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)``
10+
- ``EffectPublisher/fireAndForget(priority:_:)``
1111
- ``TaskResult``
1212

1313
### Cancellation
1414

15-
- ``cancellable(id:cancelInFlight:)-29q60``
16-
- ``cancel(id:)-6hzsl``
17-
- ``cancel(ids:)-1cqqx``
15+
- ``EffectPublisher/cancellable(id:cancelInFlight:)-29q60``
16+
- ``EffectPublisher/cancel(id:)-6hzsl``
17+
- ``EffectPublisher/cancel(ids:)-1cqqx``
1818
- ``withTaskCancellation(id:cancelInFlight:operation:)-4dtr6``
1919

2020
### Composition
2121

22-
- ``map(_:)-yn70``
23-
- ``merge(_:)-45guh``
24-
- ``merge(_:)-3d54p``
22+
- ``EffectPublisher/map(_:)-yn70``
23+
- ``EffectPublisher/merge(_:)-45guh``
24+
- ``EffectPublisher/merge(_:)-3d54p``
2525

2626
### Concurrency
2727

2828
- ``UncheckedSendable``
2929

3030
### Testing
3131

32-
- ``unimplemented(_:)``
32+
- ``EffectPublisher/unimplemented(_:)``
3333

3434
### SwiftUI integration
3535

36-
- ``animation(_:)``
36+
- ``EffectPublisher/animation(_:)``
3737

3838
### Deprecations
3939

Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
- ``receive(_:timeout:assert:file:line:)-1rwdd``
1919
- ``receive(_:timeout:assert:file:line:)-4e4m0``
2020
- ``receive(_:timeout:assert:file:line:)-3myco``
21-
- ``finish(timeout:file:line:)``
21+
- ``finish(timeout:file:line:)-53gi5``
2222
- ``TestStoreTask``
2323

2424
### Methods for skipping actions and effects

Sources/ComposableArchitecture/Effect.swift

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,51 @@ import Foundation
33
import SwiftUI
44
import XCTestDynamicOverlay
55

6-
/// A type that encapsulates a unit of work that can be run in the outside world, and can feed
7-
/// actions back to the ``Store``.
8-
///
9-
/// Effects are the perfect place to do side effects, such as network requests, saving/loading
10-
/// from disk, creating timers, interacting with dependencies, and more. They are returned from
11-
/// reducers so that the ``Store`` can perform the effects after the reducer is done running.
12-
///
13-
/// There are 2 distinct ways to create an `Effect`: one using Swift's native concurrency tools, and
14-
/// the other using Apple's Combine framework:
15-
///
16-
/// * If using Swift's native structured concurrency tools then there are 3 main ways to create an
17-
/// effect, depending on if you want to emit one single action back into the system, or any number
18-
/// of actions, or just execute some work without emitting any actions:
19-
/// * ``EffectPublisher/task(priority:operation:catch:file:fileID:line:)``
20-
/// * ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)``
21-
/// * ``EffectPublisher/fireAndForget(priority:_:)``
22-
/// * If using Combine in your application, in particular for the dependencies of your feature
23-
/// then you can create effects by making use of any of Combine's operators, and then erasing the
24-
/// publisher type to ``EffectPublisher`` with either `eraseToEffect` or `catchToEffect`. Note that
25-
/// the Combine interface to ``EffectPublisher`` is considered soft deprecated, and you should
26-
/// eventually port to Swift's native concurrency tools.
27-
///
28-
/// > Important: ``Store`` is not thread safe, and so all effects must receive values on the same
29-
/// thread. This is typically the main thread, **and** if the store is being used to drive UI then
30-
/// it must receive values on the main thread.
31-
/// >
32-
/// > This is only an issue if using the Combine interface of ``EffectPublisher`` as mentioned
33-
/// above. If you are using Swift's concurrency tools and the `.task`, `.run` and `.fireAndForget`
34-
/// functions on ``EffectTask``, then threading is automatically handled for you.
6+
/// This type is deprecated in favor of ``EffectTask``. See its documentation for more information.
7+
@available(
8+
iOS,
9+
deprecated: 9999.0,
10+
message: """
11+
'EffectPublisher' has been deprecated in favor of 'EffectTask'.
12+
13+
You are encouraged to use `EffectTask<Action>` to model the ouput of your reducers, and to use Swift concurrency to model asynchrony in dependencies.
14+
15+
See the migration roadmap for more information: https://github.com/pointfreeco/swift-composable-architecture/discussions/1477
16+
"""
17+
)
18+
@available(
19+
macOS,
20+
deprecated: 9999.0,
21+
message: """
22+
'EffectPublisher' has been deprecated in favor of 'EffectTask'.
23+
24+
You are encouraged to use `EffectTask<Action>` to model the ouput of your reducers, and to use Swift concurrency to model asynchrony in dependencies.
25+
26+
See the migration roadmap for more information: https://github.com/pointfreeco/swift-composable-architecture/discussions/1477
27+
"""
28+
)
29+
@available(
30+
tvOS,
31+
deprecated: 9999.0,
32+
message: """
33+
'EffectPublisher' has been deprecated in favor of 'EffectTask'.
34+
35+
You are encouraged to use `EffectTask<Action>` to model the ouput of your reducers, and to use Swift concurrency to model asynchrony in dependencies.
36+
37+
See the migration roadmap for more information: https://github.com/pointfreeco/swift-composable-architecture/discussions/1477
38+
"""
39+
)
40+
@available(
41+
watchOS,
42+
deprecated: 9999.0,
43+
message: """
44+
'EffectPublisher' has been deprecated in favor of 'EffectTask'.
45+
46+
You are encouraged to use `EffectTask<Action>` to model the ouput of your reducers, and to use Swift concurrency to model asynchrony in dependencies.
47+
48+
See the migration roadmap for more information: https://github.com/pointfreeco/swift-composable-architecture/discussions/1477
49+
"""
50+
)
3551
public struct EffectPublisher<Action, Failure: Error> {
3652
@usableFromInline
3753
enum Operation {
@@ -60,20 +76,38 @@ extension EffectPublisher {
6076
}
6177
}
6278

63-
/// A convenience type alias for referring to an effect that can never fail, like the kind of
64-
/// ``EffectPublisher`` returned by a reducer after processing an action.
79+
/// A type that encapsulates a unit of work that can be run in the outside world, and can feed
80+
/// actions back to the ``Store``.
6581
///
66-
/// Instead of specifying `Never` as `Failure`:
82+
/// Effects are the perfect place to do side effects, such as network requests, saving/loading
83+
/// from disk, creating timers, interacting with dependencies, and more. They are returned from
84+
/// reducers so that the ``Store`` can perform the effects after the reducer is done running.
6785
///
68-
/// ```swift
69-
/// func reduce(into state: inout State, action: Action) -> EffectPublisher<Action, Never> { … }
70-
/// ```
86+
/// There are 2 distinct ways to create an `Effect`: one using Swift's native concurrency tools, and
87+
/// the other using Apple's Combine framework:
7188
///
72-
/// You can specify a single generic:
89+
/// * If using Swift's native structured concurrency tools then there are 3 main ways to create an
90+
/// effect, depending on if you want to emit one single action back into the system, or any number
91+
/// of actions, or just execute some work without emitting any actions:
92+
/// * ``EffectPublisher/task(priority:operation:catch:file:fileID:line:)``
93+
/// * ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)``
94+
/// * ``EffectPublisher/fireAndForget(priority:_:)``
95+
/// * If using Combine in your application, in particular for the dependencies of your feature
96+
/// then you can create effects by making use of any of Combine's operators, and then erasing the
97+
/// publisher type to ``EffectPublisher`` with either `eraseToEffect` or `catchToEffect`. Note that
98+
/// the Combine interface to ``EffectPublisher`` is considered soft deprecated, and you should
99+
/// eventually port to Swift's native concurrency tools.
73100
///
74-
/// ```swift
75-
/// func reduce(into state: inout State, action: Action) -> EffectTask<Action> { … }
76-
/// ```
101+
/// > Important: The publisher interface to ``EffectTask`` is considered deperecated, and you should
102+
/// try converting any uses of that interface to Swift's native concurrency tools.
103+
/// >
104+
/// > Also, ``Store`` is not thread safe, and so all effects must receive values on the same
105+
/// thread. This is typically the main thread, **and** if the store is being used to drive UI then
106+
/// it must receive values on the main thread.
107+
/// >
108+
/// > This is only an issue if using the Combine interface of ``EffectPublisher`` as mentioned
109+
/// above. If you are using Swift's concurrency tools and the `.task`, `.run` and `.fireAndForget`
110+
/// functions on ``EffectTask``, then threading is automatically handled for you.
77111
public typealias EffectTask<Action> = Effect<Action, Never>
78112

79113
extension EffectPublisher where Failure == Never {

Sources/ComposableArchitecture/TestStore.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ public final class TestStore<State, Action, ScopedState, ScopedAction, Environme
542542
/// The default timeout used in all methods that take an optional timeout.
543543
///
544544
/// This is the default timeout used in all methods that take an optional timeout, such as
545-
/// ``receive(_:timeout:assert:file:line:)-1rwdd`` and ``finish(timeout:file:line:)``.
545+
/// ``receive(_:timeout:assert:file:line:)-1rwdd`` and ``finish(timeout:file:line:)-53gi5``.
546546
public var timeout: UInt64
547547

548548
private var _environment: Box<Environment>
@@ -1317,7 +1317,7 @@ extension TestStore where ScopedState: Equatable, Action: Equatable {
13171317
/// Asserts an action was received from an effect that matches a predicate, and asserts how the
13181318
/// state changes.
13191319
///
1320-
/// This method is similar to ``receive(_:timeout:assert:file:line:)-5n755``, except it allows
1320+
/// This method is similar to ``receive(_:timeout:assert:file:line:)-4he05``, except it allows
13211321
/// you to assert that an action was received that matches a predicate without asserting on all
13221322
/// the data in the action:
13231323
///
@@ -1336,7 +1336,7 @@ extension TestStore where ScopedState: Equatable, Action: Equatable {
13361336
/// data was in the effect that you chose not to assert on.
13371337
///
13381338
/// If you only want to check that a particular action case was received, then you might find
1339-
/// the ``receive(_:timeout:assert:file:line:)-5n755`` overload of this method more useful.
1339+
/// the ``receive(_:timeout:assert:file:line:)-4he05`` overload of this method more useful.
13401340
///
13411341
/// - Parameters:
13421342
/// - isMatching: A closure that attempts to match an action. If it returns `false`, a test
@@ -1530,7 +1530,7 @@ extension TestStore where ScopedState: Equatable, Action: Equatable {
15301530
#if swift(>=5.7) && !os(macOS) && !targetEnvironment(macCatalyst)
15311531
/// Asserts an action was received matching a case path and asserts how the state changes.
15321532
///
1533-
/// This method is similar to ``receive(_:timeout:assert:file:line:)-5n755``, except it allows
1533+
/// This method is similar to ``receive(_:timeout:assert:file:line:)-4he05``, except it allows
15341534
/// you to assert that an action was received that matches a particular case of the action enum
15351535
/// without asserting on all the data in the action.
15361536
///
@@ -1988,8 +1988,8 @@ extension TestStore {
19881988
/// await store.send(.stopTimerButtonTapped).finish()
19891989
/// ```
19901990
///
1991-
/// See ``TestStore/finish(timeout:file:line:)`` for the ability to await all in-flight effects in
1992-
/// the test store.
1991+
/// See ``TestStore/finish(timeout:file:line:)-53gi5`` for the ability to await all in-flight
1992+
/// effects in the test store.
19931993
///
19941994
/// See ``ViewStoreTask`` for the analog provided to ``ViewStore``.
19951995
public struct TestStoreTask: Hashable, Sendable {

0 commit comments

Comments
 (0)