SE-0472: Starting tasks synchronously from caller context

  • What is your evaluation of the proposal?

Big +1

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes, there exists SPI in the standard library that does a limited form of this and it is quite useful.

  • Does this proposal fit well with the feel and direction of Swift?

Yes, it especially dovetails nicely with the semantic changes to run non isolated functions on the caller's executor, in the sense that it enables more predictable control over actor isolation and enables a less surprising behavior at the call site.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I have not used other languages with async/await style concurrency enough to have run into this specific limitation.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A thorough reading, and having used the SPI variant of this.


One specific place where this will be immediately useful is being able to finally interoperate NSItemProvider loading via UIDropInteraction in UIKit code.

UIKit assumes if a client does not begin loading from a UIDragItem synchronously by the end of dropInteraction(_:performDrop:) that the client does not need to load the items, and it tears down the interaction. This nondeterminism makes it impossible to implement UIDropInteraction in a way that plays nicely with Swift concurrency.

With this API, it becomes possible:

let dataLoadTask: Task<Data, Error> = Task.startSynchronously {
    try await withCheckedThrowingContinuation { continuation in
        _ = self.loadDataRepresentation(for: type) { data, error in
            if let error {
                continuation.resume(throwing: error)
            } else if let data {
                continuation.resume(returning: data)
            }
        }
    }
}
7 Likes