URLSession Delegate Callback Order After Cancellation or Timeout
0 reputation · 05 Feb 2024, 05:33 UTC
0 reputation · 05 Feb 2024, 05:33 UTC
When a URLSession data task is cancelled via task.cancel() or by cancelling the surrounding Task in Swift 5.5’s async/await API, the underlying CFNetwork stack may still invoke delegate methods with partial data. The documentation does not specify whether urlSession(_:task:didCompleteWithError:) is guaranteed to fire after all urlSession(_:task:didReceive:) callbacks, or whether the order can vary.
urlSession(_:task:didCompleteWithError:) always occur after the last didReceive callback when a task is cancelled?data(for:) call return any data before throwing URLError.cancelled?32775 reputation · 05 Feb 2024, 15:31 UTC
In a delegate-based URLSessionDataTask, urlSession(_:task:didCompleteWithError:) is the terminal callback of the task lifecycle. It is guaranteed to be the final method called for a given task, occurring after all urlSession(_:task:didReceive:) callbacks have finished executing.
When a task is cancelled via task.cancel(), the following sequence occurs:
didReceive data callbacks.didCompleteWithError: with a URLError.cancelled code.The completion callback will not be invoked before final data callbacks; it serves as the definitive signal that no further data will be delivered for that task.
The behavior of the async data(for:) method differs fundamentally from the delegate pattern because it is designed as an atomic operation rather than a stream.
data(for:) does not return partial data. It either returns the complete Data object upon successful completion or throws an error.Task is cancelled while data(for:) is awaiting a response, the method will throw URLError.cancelled immediately. It will not return any partially received data.To verify this behavior in your specific environment (assuming iOS 15+ or macOS 12+), you can use the following scoped tests:
URLSessionDataDelegate and add print statements with timestamps to both didReceive data and didCompleteWithError. Call task.cancel() during a large download to confirm the completion handler always prints last.let (data, response) = try await session.data(for: request) in a Task, call task.cancel() after a short delay, and verify that the catch block captures the cancellation error without any data being returned.Diagnostic Note: Are you using a URLSessionConfiguration.background session? Background sessions handle delegate callbacks differently (often via the app delegate) and may exhibit different timing regarding when the system wakes the app to deliver the final completion error.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.