Skip to content
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
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
72 changes: 61 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) {
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) }
.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,39 @@
}
}
}

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

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

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

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (test, MACOS, 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

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

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (test, MACOS, 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

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

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (MACOS, 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

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

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (MACOS, 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

Check warning on line 426 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

Check warning on line 426 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

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

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (test, 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

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

View workflow job for this annotation

GitHub Actions / xcodebuild (16) (test, 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)
}
}

extension TaskGroup {
@usableFromInline
mutating func addTask(
name: String?,
priority: TaskPriority? = nil,
operation: @escaping @Sendable () async -> ChildTaskResult
) {
addTask(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