# Cancellation and AsyncSequence — Swift

Source: https://www.skillbyai.com/en/swift/c-sequences

> Cancel tasks cooperatively and consume asynchronous streams of values.

## Stopping work and handling streams

**Cancellation** in Swift is **cooperative**: calling `task.cancel()` sets a flag, and well-behaved async code checks it with `try Task.checkCancellation()` (which throws `CancellationError`) or `Task.isCancelled`, then stops promptly. Many system APIs, such as `URLSession` and `Task.sleep`, respond to cancellation automatically. Structured concurrency propagates cancellation to child tasks, and SwiftUI's `.task` modifier cancels its task when the view disappears. An **`AsyncSequence`** delivers values over time and is consumed with **`for await`**: lines of a file, bytes from a network response, notifications or values from `URL.lines`. **`AsyncStream`** bridges callback- or delegate-based APIs into async sequences: you yield values through a continuation and finish when done. For one-shot callback APIs, `withCheckedContinuation` and `withCheckedThrowingContinuation` wrap a completion handler into an `async` function; you must resume each continuation exactly once. Together these tools let you modernise older APIs without rewriting them.

## Cancellation checks and an AsyncStream

Long work stops when cancelled; a callback API becomes an async sequence.

```swift
func processImages(_ urls: [URL]) async throws -> Int {
    var processed = 0
    for url in urls {
        try Task.checkCancellation()                 // stop promptly if cancelled
        try await resizeAndUpload(url)
        processed += 1
    }
    return processed
}

let job = Task { try await processImages(selectedURLs) }
// later, e.g. when the user taps Cancel:
job.cancel()

func locationUpdates(from provider: LocationProvider) -> AsyncStream<Coordinate> {
    AsyncStream { continuation in
        provider.onUpdate = { coordinate in continuation.yield(coordinate) }
        provider.onStop = { continuation.finish() }
        continuation.onTermination = { _ in provider.stop() }   // consumer stopped listening
        provider.start()
    }
}

for await coordinate in locationUpdates(from: provider) {
    print(coordinate)
}

func legacyFetch(id: String) async throws -> Data {
    try await withCheckedThrowingContinuation { continuation in
        oldClient.fetch(id) { result in continuation.resume(with: result) }   // resume exactly once
    }
}
```

## Resume continuations exactly once

Resuming a checked continuation twice crashes, and never resuming it leaves the awaiting task suspended forever. Make sure every code path in the callback resumes it once.

**Quiz:** How does cancellation work in Swift concurrency?

- [ ] It forcibly kills the thread
- [ ] It restarts the task
- [x] It is cooperative: tasks check for cancellation and stop themselves
- [ ] It is not supported

*Answer:* It is cooperative: tasks check for cancellation and stop themselves. cancel() sets a flag that code observes via checkCancellation or isCancelled.
