Skip to content

Commit a851f82

Browse files
Add support for naming tasks in .run effects (#3784)
* Add support for naming tasks in .run effects * Cleanup * fix * typo * wip * wip --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent f080048 commit a851f82

File tree

8 files changed

+84
-34
lines changed

8 files changed

+84
-34
lines changed

Sources/ComposableArchitecture/Core.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ final class RootCore<Root: Reducer>: Core {
136136
task.cancel()
137137
}
138138
}
139-
case let .run(priority, operation):
139+
case let .run(name, priority, operation):
140140
withEscapedDependencies { continuation in
141-
let task = Task(priority: priority) { @MainActor [weak self] in
141+
let task = Task(name: name, priority: priority) { @MainActor [weak self] in
142142
let isCompleted = LockIsolated(false)
143143
defer { isCompleted.setValue(true) }
144144
await operation(

Sources/ComposableArchitecture/Effect.swift

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ public struct Effect<Action>: Sendable {
77
enum Operation: Sendable {
88
case none
99
case publisher(AnyPublisher<Action, Never>)
10-
case run(TaskPriority? = nil, @Sendable (_ send: Send<Action>) async -> Void)
10+
case run(
11+
name: String? = nil,
12+
priority: TaskPriority? = nil,
13+
operation: @Sendable (_ send: Send<Action>) async -> Void
14+
)
1115
}
1216

1317
@usableFromInline
@@ -76,6 +80,7 @@ extension Effect {
7680
/// - Parameters:
7781
/// - priority: Priority of the underlying task. If `nil`, the priority will come from
7882
/// `Task.currentPriority`.
83+
/// - name: An optional name to associate with the task that runs this effect.
7984
/// - operation: The operation to execute.
8085
/// - handler: An error handler, invoked if the operation throws an error other than
8186
/// `CancellationError`.
@@ -86,6 +91,7 @@ extension Effect {
8691
/// - Returns: An effect wrapping the given asynchronous work.
8792
public static func run(
8893
priority: TaskPriority? = nil,
94+
name: String? = nil,
8995
operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,
9096
catch handler: (@Sendable (_ error: any Error, _ send: Send<Action>) async -> Void)? = nil,
9197
fileID: StaticString = #fileID,
@@ -95,7 +101,7 @@ extension Effect {
95101
) -> Self {
96102
withEscapedDependencies { escaped in
97103
Self(
98-
operation: .run(priority) { send in
104+
operation: .run(name: name, priority: priority) { send in
99105
await escaped.yield {
100106
do {
101107
try await operation(send)
@@ -268,14 +274,17 @@ extension Effect {
268274
.eraseToAnyPublisher()
269275
)
270276
)
271-
case let (.run(lhsPriority, lhsOperation), .run(rhsPriority, rhsOperation)):
277+
case (
278+
.run(let lhsName, let lhsPriority, let lhsOperation),
279+
.run(let rhsName, let rhsPriority, let rhsOperation)
280+
):
272281
return Self(
273282
operation: .run { send in
274283
await withTaskGroup(of: Void.self) { group in
275-
group.addTask(priority: lhsPriority) {
284+
group.addTask(name: lhsName, priority: lhsPriority) {
276285
await lhsOperation(send)
277286
}
278-
group.addTask(priority: rhsPriority) {
287+
group.addTask(name: rhsName, priority: rhsPriority) {
279288
await rhsOperation(send)
280289
}
281290
}
@@ -328,16 +337,21 @@ extension Effect {
328337
.eraseToAnyPublisher()
329338
)
330339
)
331-
case let (.run(lhsPriority, lhsOperation), .run(rhsPriority, rhsOperation)):
340+
case (
341+
.run(let lhsName, let lhsPriority, let lhsOperation),
342+
.run(let rhsName, let rhsPriority, let rhsOperation)
343+
):
332344
return Self(
333345
operation: .run { send in
334346
if let lhsPriority {
335-
await Task(priority: lhsPriority) { await lhsOperation(send) }.cancellableValue
347+
await Task(name: lhsName, priority: lhsPriority) { await lhsOperation(send) }
348+
.cancellableValue
336349
} else {
337350
await lhsOperation(send)
338351
}
339352
if let rhsPriority {
340-
await Task(priority: rhsPriority) { await rhsOperation(send) }.cancellableValue
353+
await Task(name: rhsName, priority: rhsPriority) { await rhsOperation(send) }
354+
.cancellableValue
341355
} else {
342356
await rhsOperation(send)
343357
}
@@ -356,7 +370,7 @@ extension Effect {
356370
switch self.operation {
357371
case .none:
358372
return .none
359-
case let .publisher(publisher):
373+
case .publisher(let publisher):
360374
return .init(
361375
operation: .publisher(
362376
publisher
@@ -372,10 +386,10 @@ extension Effect {
372386
.eraseToAnyPublisher()
373387
)
374388
)
375-
case let .run(priority, operation):
389+
case .run(let name, let priority, let operation):
376390
return withEscapedDependencies { escaped in
377391
.init(
378-
operation: .run(priority) { send in
392+
operation: .run(name: name, priority: priority) { send in
379393
await escaped.yield {
380394
await operation(
381395
Send { action in
@@ -389,3 +403,39 @@ extension Effect {
389403
}
390404
}
391405
}
406+
407+
#if swift(<6.2)
408+
// NB: Backwards-compatible shims.
409+
extension Task {
410+
@discardableResult
411+
@usableFromInline
412+
init(
413+
name: String?,
414+
priority: TaskPriority? = nil,
415+
operation: @escaping @Sendable () async -> Success
416+
) where Failure == Never {
417+
self.init(priority: priority, operation: operation)
418+
}
419+
420+
@discardableResult
421+
@usableFromInline
422+
init(
423+
name: String?,
424+
priority: TaskPriority? = nil,
425+
operation: @escaping @Sendable () async throws -> Success
426+
) where Failure == Error {
427+
self.init(priority: priority, operation: operation)
428+
}
429+
}
430+
431+
extension TaskGroup {
432+
@usableFromInline
433+
mutating func addTask(
434+
name: String?,
435+
priority: TaskPriority? = nil,
436+
operation: @escaping @Sendable () async -> ChildTaskResult
437+
) {
438+
addTask(priority: priority, operation: operation)
439+
}
440+
}
441+
#endif

Sources/ComposableArchitecture/Effects/Animation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ extension Effect {
4242
TransactionPublisher(upstream: publisher, transaction: transaction).eraseToAnyPublisher()
4343
)
4444
)
45-
case let .run(priority, operation):
45+
case let .run(name, priority, operation):
4646
let uncheckedTransaction = UncheckedSendable(transaction)
4747
return Self(
48-
operation: .run(priority) { send in
48+
operation: .run(name: name, priority: priority) { send in
4949
await operation(
5050
Send { value in
5151
withTransaction(uncheckedTransaction.value) {

Sources/ComposableArchitecture/Effects/Cancellation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ extension Effect {
8383
.eraseToAnyPublisher()
8484
)
8585
)
86-
case let .run(priority, operation):
86+
case let .run(name, priority, operation):
8787
return withEscapedDependencies { continuation in
8888
return Self(
89-
operation: .run(priority) { send in
89+
operation: .run(name: name, priority: priority) { send in
9090
await continuation.yield {
9191
await withTaskCancellation(id: id, cancelInFlight: cancelInFlight) {
9292
await operation(send)

Sources/ComposableArchitecture/Effects/Publisher.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public struct _EffectPublisher<Action>: Publisher {
3030
return Empty().eraseToAnyPublisher()
3131
case let .publisher(publisher):
3232
return publisher
33-
case let .run(priority, operation):
33+
case let .run(name, priority, operation):
3434
return .create { subscriber in
35-
let task = Task(priority: priority) { @MainActor in
35+
let task = Task(name: name, priority: priority) { @MainActor in
3636
defer { subscriber.send(completion: .finished) }
3737
await operation(Send { subscriber.send($0) })
3838
}

Sources/ComposableArchitecture/Internal/EffectActions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ extension Effect where Action: Sendable {
1515
cancellable.cancel()
1616
}
1717
}
18-
case let .run(priority, operation):
18+
case let .run(name, priority, operation):
1919
return AsyncStream { continuation in
20-
let task = Task(priority: priority) {
20+
let task = Task(name: name, priority: priority) {
2121
await operation(Send { action in continuation.yield(action) })
2222
continuation.finish()
2323
}

Sources/ComposableArchitecture/Reducer/Reducers/SignpostReducer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ extension Effect {
114114
.eraseToAnyPublisher()
115115
)
116116
)
117-
case let .run(priority, operation):
117+
case let .run(name, priority, operation):
118118
return .init(
119-
operation: .run(priority) { send in
119+
operation: .run(name: name, priority: priority) { send in
120120
os_signpost(
121121
.begin, log: log, name: "Effect", signpostID: sid, "%sStarted from %s", prefix,
122122
actionOutput

Tests/ComposableArchitectureTests/EffectOperationTests.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
effect = Effect<Int>.run { send in await send(42) }
1818
.merge(with: .none)
1919
switch effect.operation {
20-
case let .run(_, send):
20+
case let .run(_, _, send):
2121
await send(.init(send: { XCTAssertEqual($0, 42) }))
2222
default:
2323
XCTFail()
@@ -26,7 +26,7 @@
2626
effect = Effect<Int>.none
2727
.merge(with: .run { send in await send(42) })
2828
switch effect.operation {
29-
case let .run(_, send):
29+
case let .run(_, _, send):
3030
await send(.init(send: { XCTAssertEqual($0, 42) }))
3131
default:
3232
XCTFail()
@@ -35,7 +35,7 @@
3535
effect = Effect<Int>.run { await $0(42) }
3636
.merge(with: .none)
3737
switch effect.operation {
38-
case let .run(_, send):
38+
case let .run(_, _, send):
3939
await send(.init(send: { XCTAssertEqual($0, 42) }))
4040
default:
4141
XCTFail()
@@ -44,7 +44,7 @@
4444
effect = Effect<Int>.none
4545
.merge(with: .run { await $0(42) })
4646
switch effect.operation {
47-
case let .run(_, send):
47+
case let .run(_, _, send):
4848
await send(.init(send: { XCTAssertEqual($0, 42) }))
4949
default:
5050
XCTFail()
@@ -64,7 +64,7 @@
6464
effect = Effect<Int>.run { send in await send(42) }
6565
.concatenate(with: .none)
6666
switch effect.operation {
67-
case let .run(_, send):
67+
case let .run(_, _, send):
6868
await send(.init(send: { XCTAssertEqual($0, 42) }))
6969
default:
7070
XCTFail()
@@ -73,7 +73,7 @@
7373
effect = Effect<Int>.none
7474
.concatenate(with: .run { send in await send(42) })
7575
switch effect.operation {
76-
case let .run(_, send):
76+
case let .run(_, _, send):
7777
await send(.init(send: { XCTAssertEqual($0, 42) }))
7878
default:
7979
XCTFail()
@@ -82,7 +82,7 @@
8282
effect = Effect<Int>.run { send in await send(42) }
8383
.concatenate(with: .none)
8484
switch effect.operation {
85-
case let .run(_, send):
85+
case let .run(_, _, send):
8686
await send(.init(send: { XCTAssertEqual($0, 42) }))
8787
default:
8888
XCTFail()
@@ -91,7 +91,7 @@
9191
effect = Effect<Int>.none
9292
.concatenate(with: .run { send in await send(42) })
9393
switch effect.operation {
94-
case let .run(_, send):
94+
case let .run(_, _, send):
9595
await send(.init(send: { XCTAssertEqual($0, 42) }))
9696
default:
9797
XCTFail()
@@ -113,7 +113,7 @@
113113
}
114114
)
115115
switch effect.operation {
116-
case let .run(_, send):
116+
case let .run(_, _, send):
117117
await send(.init { values.append($0) })
118118
default:
119119
XCTFail()
@@ -129,7 +129,7 @@
129129
let effect = Effect<Int>.run { send in await send(42) }
130130
.concatenate(with: .run { send in await send(1729) })
131131
switch effect.operation {
132-
case let .run(_, send):
132+
case let .run(_, _, send):
133133
await send(.init(send: { values.append($0) }))
134134
default:
135135
XCTFail()
@@ -143,7 +143,7 @@
143143
.map { "\($0)" }
144144

145145
switch effect.operation {
146-
case let .run(_, send):
146+
case let .run(_, _, send):
147147
await send(.init(send: { XCTAssertEqual($0, "42") }))
148148
default:
149149
XCTFail()

0 commit comments

Comments
 (0)