Skip to content
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

Rollback Effect<Action>.Send to a top level EffectSend<Action> #1930

Merged
merged 7 commits into from
Mar 6, 2023
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
126 changes: 62 additions & 64 deletions Sources/ComposableArchitecture/Effect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public struct EffectPublisher<Action, Failure: Error> {
enum Operation {
case none
case publisher(AnyPublisher<Action, Failure>)
case run(TaskPriority? = nil, @Sendable (Send) async -> Void)
case run(TaskPriority? = nil, @Sendable (Send<Action>) async -> Void)
}

@usableFromInline
Expand Down Expand Up @@ -253,8 +253,8 @@ extension EffectPublisher where Failure == Never {
/// - Returns: An effect wrapping the given asynchronous work.
public static func run(
priority: TaskPriority? = nil,
operation: @escaping @Sendable (Send) async throws -> Void,
catch handler: (@Sendable (Error, Send) async -> Void)? = nil,
operation: @escaping @Sendable (Send<Action>) async throws -> Void,
catch handler: (@Sendable (Error, Send<Action>) async -> Void)? = nil,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
Expand Down Expand Up @@ -353,70 +353,68 @@ extension EffectPublisher where Failure == Never {
}
}

extension EffectTask {
/// A type that can send actions back into the system when used from
/// ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)``.
///
/// This type implements [`callAsFunction`][callAsFunction] so that you invoke it as a function
/// rather than calling methods on it:
///
/// ```swift
/// return .run { send in
/// send(.started)
/// defer { send(.finished) }
/// for await event in self.events {
/// send(.event(event))
/// }
/// }
/// ```
///
/// You can also send actions with animation:
///
/// ```swift
/// send(.started, animation: .spring())
/// defer { send(.finished, animation: .default) }
/// ```
///
/// See ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)`` for more information on how to
/// use this value to construct effects that can emit any number of times in an asynchronous
/// context.
///
/// [callAsFunction]: https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID622
@MainActor
public struct Send {
public let send: @MainActor (Action) -> Void
/// A type that can send actions back into the system when used from
/// ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)``.
///
/// This type implements [`callAsFunction`][callAsFunction] so that you invoke it as a function
/// rather than calling methods on it:
///
/// ```swift
/// return .run { send in
/// send(.started)
/// defer { send(.finished) }
/// for await event in self.events {
/// send(.event(event))
/// }
/// }
/// ```
///
/// You can also send actions with animation:
///
/// ```swift
/// send(.started, animation: .spring())
/// defer { send(.finished, animation: .default) }
/// ```
///
/// See ``EffectPublisher/run(priority:operation:catch:file:fileID:line:)`` for more information on how to
/// use this value to construct effects that can emit any number of times in an asynchronous
/// context.
///
/// [callAsFunction]: https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID622
@MainActor
public struct Send<Action> {
public let send: @MainActor (Action) -> Void

public init(send: @escaping @MainActor (Action) -> Void) {
self.send = send
}
public init(send: @escaping @MainActor (Action) -> Void) {
self.send = send
}

/// Sends an action back into the system from an effect.
///
/// - Parameter action: An action.
public func callAsFunction(_ action: Action) {
guard !Task.isCancelled else { return }
self.send(action)
}
/// Sends an action back into the system from an effect.
///
/// - Parameter action: An action.
public func callAsFunction(_ action: Action) {
guard !Task.isCancelled else { return }
self.send(action)
}

/// Sends an action back into the system from an effect with animation.
///
/// - Parameters:
/// - action: An action.
/// - animation: An animation.
public func callAsFunction(_ action: Action, animation: Animation?) {
callAsFunction(action, transaction: Transaction(animation: animation))
}
/// Sends an action back into the system from an effect with animation.
///
/// - Parameters:
/// - action: An action.
/// - animation: An animation.
public func callAsFunction(_ action: Action, animation: Animation?) {
callAsFunction(action, transaction: Transaction(animation: animation))
}

/// Sends an action back into the system from an effect with transaction.
///
/// - Parameters:
/// - action: An action.
/// - transaction: A transaction.
public func callAsFunction(_ action: Action, transaction: Transaction) {
guard !Task.isCancelled else { return }
withTransaction(transaction) {
self(action)
}
/// Sends an action back into the system from an effect with transaction.
///
/// - Parameters:
/// - action: An action.
/// - transaction: A transaction.
public func callAsFunction(_ action: Action, transaction: Transaction) {
guard !Task.isCancelled else { return }
withTransaction(transaction) {
self(action)
}
}
}
Expand Down Expand Up @@ -563,7 +561,7 @@ extension EffectPublisher {
operation: .run(priority) { send in
await escaped.yield {
await operation(
Send { action in
Send<Action> { action in
send(transform(action))
}
)
Expand Down
2 changes: 1 addition & 1 deletion Sources/ComposableArchitecture/Effects/Publisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension EffectPublisher: Publisher {
var isCompleted = false
defer { isCompleted = true }
#endif
let send = Send {
let send = Send<Action> {
#if DEBUG
if isCompleted {
runtimeWarn(
Expand Down
19 changes: 11 additions & 8 deletions Sources/ComposableArchitecture/Internal/Deprecations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import Combine
import SwiftUI
import XCTestDynamicOverlay

// MARK: - Deprecated after 0.50.4

@available(
*,
deprecated,
message: "Use 'EffectTask<Action>.Send' instead."
)
public typealias Send<Action> = EffectTask<Action>.Send
// MARK: - Deprecated after 0.51.0

// NB Soft deprecation to avoid deprecation messages when this type is inferred in `Effect`
// without having to qualify it as `ComposableArchitecture.Send` there.
@available(iOS, deprecated: 9999.0, message: "Use 'Send<Action>' instead.")
@available(macOS, deprecated: 9999.0, message: "Use 'Send<Action>' instead.")
@available(tvOS, deprecated: 9999.0, message: "Use 'Send<Action>' instead.")
@available(watchOS, deprecated: 9999.0, message: "Use 'Send<Action>' instead.")
extension EffectTask {
public typealias Send = ComposableArchitecture.Send
}
Comment on lines +6 to +16
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reinstalls a soft-deprecated EffectTask.Send in any case someone relied on it in a context where Action was furthermore inferred.
This can probably be removed.


// MARK: - Deprecated after 0.49.2

Expand Down
2 changes: 1 addition & 1 deletion Sources/ComposableArchitecture/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ public final class Store<State, Action> {
defer { isCompleted = true }
#endif
await operation(
EffectTask<Action>.Send { effectAction in
Send { effectAction in
#if DEBUG
if isCompleted {
runtimeWarn(
Expand Down