Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions Sources/ComposableArchitecture/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ final class RootCore<Root: Reducer>: Core {
task.cancel()
}
}
case let .run(priority, operation):
case let .run(name, priority, operation):
withEscapedDependencies { continuation in
let task = Task(priority: priority) { @MainActor [weak self] in
let task = Task(name: name, priority: priority) { @MainActor [weak self] in
let isCompleted = LockIsolated(false)
defer { isCompleted.setValue(true) }
await operation(
Expand Down
59 changes: 48 additions & 11 deletions Sources/ComposableArchitecture/Effect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
enum Operation: Sendable {
case none
case publisher(AnyPublisher<Action, Never>)
case run(TaskPriority? = nil, @Sendable (_ send: Send<Action>) async -> Void)
case run(
name: String? = nil,
priority: TaskPriority? = nil,
operation: @Sendable (_ send: Send<Action>) async -> Void
)
}

@usableFromInline
Expand Down Expand Up @@ -76,6 +80,7 @@
/// - Parameters:
/// - priority: Priority of the underlying task. If `nil`, the priority will come from
/// `Task.currentPriority`.
/// - name: An optional name to associate with the task that runs this effect.
/// - operation: The operation to execute.
/// - handler: An error handler, invoked if the operation throws an error other than
/// `CancellationError`.
Expand All @@ -86,6 +91,7 @@
/// - Returns: An effect wrapping the given asynchronous work.
public static func run(
priority: TaskPriority? = nil,
name: String? = nil,
operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,
catch handler: (@Sendable (_ error: any Error, _ send: Send<Action>) async -> Void)? = nil,
fileID: StaticString = #fileID,
Expand All @@ -95,7 +101,7 @@
) -> Self {
withEscapedDependencies { escaped in
Self(
operation: .run(priority) { send in
operation: .run(name: name, priority: priority) { send in
await escaped.yield {
do {
try await operation(send)
Expand Down Expand Up @@ -268,14 +274,17 @@
.eraseToAnyPublisher()
)
)
case let (.run(lhsPriority, lhsOperation), .run(rhsPriority, rhsOperation)):
case (
.run(let lhsName, let lhsPriority, let lhsOperation),
.run(let rhsName, let rhsPriority, let rhsOperation)
):
return Self(
operation: .run { send in
await withTaskGroup(of: Void.self) { group in
group.addTask(priority: lhsPriority) {
group.addTask(name: lhsName, priority: lhsPriority) {

Check failure on line 284 in Sources/ComposableArchitecture/Effect.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (IOS, 16.4)

incorrect argument label in call (have 'name:priority:_:', expected 'executorPreference:priority:operation:')
await lhsOperation(send)
}
group.addTask(priority: rhsPriority) {
group.addTask(name: rhsName, priority: rhsPriority) {
await rhsOperation(send)
}
}
Expand Down Expand Up @@ -328,16 +337,21 @@
.eraseToAnyPublisher()
)
)
case let (.run(lhsPriority, lhsOperation), .run(rhsPriority, rhsOperation)):
case (
.run(let lhsName, let lhsPriority, let lhsOperation),
.run(let rhsName, let rhsPriority, let rhsOperation)
):
return Self(
operation: .run { send in
if let lhsPriority {
await Task(priority: lhsPriority) { await lhsOperation(send) }.cancellableValue
await Task(name: lhsName, priority: lhsPriority) { await lhsOperation(send) }

Check failure on line 347 in Sources/ComposableArchitecture/Effect.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (IOS, 16.4)

value of optional type 'String?' must be unwrapped to a value of type 'String'
.cancellableValue
} else {
await lhsOperation(send)
}
if let rhsPriority {
await Task(priority: rhsPriority) { await rhsOperation(send) }.cancellableValue
await Task(name: rhsName, priority: rhsPriority) { await rhsOperation(send) }
.cancellableValue
} else {
await rhsOperation(send)
}
Expand All @@ -356,7 +370,7 @@
switch self.operation {
case .none:
return .none
case let .publisher(publisher):
case .publisher(let publisher):
return .init(
operation: .publisher(
publisher
Expand All @@ -372,10 +386,10 @@
.eraseToAnyPublisher()
)
)
case let .run(priority, operation):
case .run(let name, let priority, let operation):
return withEscapedDependencies { escaped in
.init(
operation: .run(priority) { send in
operation: .run(name: name, priority: priority) { send in
await escaped.yield {
await operation(
Send { action in
Expand All @@ -389,3 +403,26 @@
}
}
}

#if swift(<6.2)
// NB: Backwards-compatible shims.
extension Task {
@discardableResult
init(
name: String,
priority: TaskPriority? = nil,
operation: @escaping @Sendable () async -> Success
) where Failure == Never {
self.init(priority: priority, operation: operation)
}

@discardableResult
init(
name: String,
priority: TaskPriority? = nil,
operation: @escaping @Sendable () async throws -> Success
) where Failure == Error {

Check warning on line 424 in Sources/ComposableArchitecture/Effect.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (IOS, 16.4)

use of protocol 'Error' as a type must be written 'any Error'; this will be an error in a future Swift language mode
self.init(priority: priority, operation: operation)
}
}
#endif
4 changes: 2 additions & 2 deletions Sources/ComposableArchitecture/Effects/Animation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ extension Effect {
TransactionPublisher(upstream: publisher, transaction: transaction).eraseToAnyPublisher()
)
)
case let .run(priority, operation):
case let .run(name, priority, operation):
let uncheckedTransaction = UncheckedSendable(transaction)
return Self(
operation: .run(priority) { send in
operation: .run(name: name, priority: priority) { send in
await operation(
Send { value in
withTransaction(uncheckedTransaction.value) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/ComposableArchitecture/Effects/Cancellation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ extension Effect {
.eraseToAnyPublisher()
)
)
case let .run(priority, operation):
case let .run(name, priority, operation):
return withEscapedDependencies { continuation in
return Self(
operation: .run(priority) { send in
operation: .run(name: name, priority: priority) { send in
await continuation.yield {
await withTaskCancellation(id: id, cancelInFlight: cancelInFlight) {
await operation(send)
Expand Down
4 changes: 2 additions & 2 deletions Sources/ComposableArchitecture/Effects/Publisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public struct _EffectPublisher<Action>: Publisher {
return Empty().eraseToAnyPublisher()
case let .publisher(publisher):
return publisher
case let .run(priority, operation):
case let .run(name, priority, operation):
return .create { subscriber in
let task = Task(priority: priority) { @MainActor in
let task = Task(name: name, priority: priority) { @MainActor in
defer { subscriber.send(completion: .finished) }
await operation(Send { subscriber.send($0) })
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/ComposableArchitecture/Internal/EffectActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ extension Effect where Action: Sendable {
cancellable.cancel()
}
}
case let .run(priority, operation):
case let .run(name, priority, operation):
return AsyncStream { continuation in
let task = Task(priority: priority) {
let task = Task(name: name, priority: priority) {
await operation(Send { action in continuation.yield(action) })
continuation.finish()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ extension Effect {
.eraseToAnyPublisher()
)
)
case let .run(priority, operation):
case let .run(name, priority, operation):
return .init(
operation: .run(priority) { send in
operation: .run(name: name, priority: priority) { send in
os_signpost(
.begin, log: log, name: "Effect", signpostID: sid, "%sStarted from %s", prefix,
actionOutput
Expand Down
22 changes: 11 additions & 11 deletions Tests/ComposableArchitectureTests/EffectOperationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
effect = Effect<Int>.run { send in await send(42) }
.merge(with: .none)
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -26,7 +26,7 @@
effect = Effect<Int>.none
.merge(with: .run { send in await send(42) })
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -35,7 +35,7 @@
effect = Effect<Int>.run { await $0(42) }
.merge(with: .none)
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -44,7 +44,7 @@
effect = Effect<Int>.none
.merge(with: .run { await $0(42) })
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -64,7 +64,7 @@
effect = Effect<Int>.run { send in await send(42) }
.concatenate(with: .none)
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -73,7 +73,7 @@
effect = Effect<Int>.none
.concatenate(with: .run { send in await send(42) })
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -82,7 +82,7 @@
effect = Effect<Int>.run { send in await send(42) }
.concatenate(with: .none)
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -91,7 +91,7 @@
effect = Effect<Int>.none
.concatenate(with: .run { send in await send(42) })
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, 42) }))
default:
XCTFail()
Expand All @@ -113,7 +113,7 @@
}
)
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init { values.append($0) })
default:
XCTFail()
Expand All @@ -129,7 +129,7 @@
let effect = Effect<Int>.run { send in await send(42) }
.concatenate(with: .run { send in await send(1729) })
switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { values.append($0) }))
default:
XCTFail()
Expand All @@ -143,7 +143,7 @@
.map { "\($0)" }

switch effect.operation {
case let .run(_, send):
case let .run(_, _, send):
await send(.init(send: { XCTAssertEqual($0, "42") }))
default:
XCTFail()
Expand Down
Loading