From 55c2300e41f80086d6ee29c79597d12361464f55 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 22 Aug 2025 10:53:20 -0500 Subject: [PATCH] Better way to detect cancellation. --- Sources/ComposableArchitecture/Effect.swift | 2 ++ .../EffectCancellationTests.swift | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/ComposableArchitecture/Effect.swift b/Sources/ComposableArchitecture/Effect.swift index 52f84c5bdee5..965565d50a14 100644 --- a/Sources/ComposableArchitecture/Effect.swift +++ b/Sources/ComposableArchitecture/Effect.swift @@ -102,6 +102,8 @@ extension Effect { } catch is CancellationError { return } catch { + guard !Task.isCancelled + else { return } guard let handler else { reportIssue( """ diff --git a/Tests/ComposableArchitectureTests/EffectCancellationTests.swift b/Tests/ComposableArchitectureTests/EffectCancellationTests.swift index 4f33cb139899..7fe5fd49bb09 100644 --- a/Tests/ComposableArchitectureTests/EffectCancellationTests.swift +++ b/Tests/ComposableArchitectureTests/EffectCancellationTests.swift @@ -285,6 +285,22 @@ final class EffectCancellationTests: BaseTCATestCase { } XCTAssertEqual(output, [1, 2]) } + + @available(iOS 15.0, *) + func testCancellationWithoutThrowingCancellationError() async throws { + let effect = Effect.run { send in + let session = URLSession(configuration: .ephemeral) + let request = URLRequest(url: URL(string: "http://ipv4.download.thinkbroadband.com/1GB.zip")!) + let (data, response) = try await session.data(for: request, delegate: nil) + _ = (data, response) + } + .cancellable(id: 1) + Task { + for await _ in effect.actions {} + } + try await Task.sleep(nanoseconds: 10_000_000) + Task.cancel(id: 1) + } } #if DEBUG @@ -350,7 +366,8 @@ final class EffectCancellationTests: BaseTCATestCase { .publisher { Just(idx) .delay( - for: .milliseconds(Int.random(in: 1...100)), scheduler: queues.randomElement()! + for: .milliseconds(Int.random(in: 1...100)), + scheduler: queues.randomElement()! ) } .cancellable(id: id), @@ -358,7 +375,8 @@ final class EffectCancellationTests: BaseTCATestCase { .publisher { Empty() .delay( - for: .milliseconds(Int.random(in: 1...100)), scheduler: queues.randomElement()! + for: .milliseconds(Int.random(in: 1...100)), + scheduler: queues.randomElement()! ) .handleEvents(receiveCompletion: { _ in Task.cancel(id: id) }) }