This code crashes without the @Sendable annotation. What can I learn from this?

I’m dealing with some code where if I forget to annotate it as @Sendablethen it crashes when ran, 100% every time. In general the code looks like this:

return await withCheckedContinuation { continuation in
    some3rdPartyClosure { @Sendable timedOut in // If I leave off Sendable here, it crashes
        …
        continuation.resume()
    }
}

What can I extract as learnings here? Why do I need to annotate this as @Sendable. Why are there no compiler warnings here? And most importantly what is causing the crash?

The specific third party code that needs to be annotated as @Sendable

Edit: The above was a comment as to the workaround, this is the actual code

The third party library is probably calling the closure on a different thread than you provided it on. The simplest version of this would look something like this and would compile in Swift 5:

func runClosure(_ fn: () -> ()) {
    DispatchQueue().async {
        fn()
    }
}

This is a violation of Swift 6’s rules; the closure wasn’t sendable, so it shouldn’t have been “sent” to another thread.

I believe future versions of Swift assume that closures passed to libraries like this are Sendable, but I don’t have a citation for that, and it might only apply to libraries that are bridged from Objective-C.

This has been an issue with folks using Swift Concurrency with Apple libraries like User notification center that don’t follow the rules yet.

3 Likes

That only applies to closures that are imported as completion handlers:

The Objective-C library can mark the closure as NS_SWIFT_SENDING or NS_SWIFT_SENDABLE to add the appropriate annotation.

2 Likes