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) }) }