[Accepted] SE-0463: Import Objective-C completion handler parameters as `@Sendable`

Hello, Swift community.

The review of SE-0463: Import Objective-C completion handler parameters as @Sendable ran from February 27th through March 10th, 2025.

Feedback on the review was light but positive. The Language Steering Group is in accord with the community, and SE-0463 has been accepted.

I'd like to thank the reviewers for taking the time to read the proposal, ask questions, and leave feedback on this proposal; it may not seem like much, but these contributions are a vital part of ensuring the quality of the language.

John McCall
Review Manager

14 Likes

I keep encountering this issue. Here's today's example:

import SwiftUI
import AVFoundation

struct RecordingView: View {
  // ...
 
  func recordButtonPressed() {

    AVAudioApplication.requestRecordPermission { granted in
      guard granted else {
        self.error = .recordingPermissionDenied
        return
      }
      self.recorder.startRecording()
    }
  }

}

If the user has already granted the app recording permissions, AVAudioApplication.requestRecordPermission calls the handler closure synchronously. If the app does not have permissions already granted, the framework will request those permissions from the user and call the handler closure on an arbitrary thread.

Worse is that it seems this information has been lost as APIs have been shuffled around. The docs for AVAudioApplication.requestRecordPermission do not mention this at all, but if you dig long enough, eventually you'll find its now-deprecated predecessor on AVAudioSession, which does mention it:

The framework may call the thread from a different thread context. Dispatch back to the main thread for any changes that update the user interface.


The rate at which I'm encountering these errors is alarming. I consider this an exceptionally severe bug, to the point where I feel Swift Concurrency is not usable (certainly in Apple's SDKs) until a fix is available in a shipping release. Anybody who ships code using Swift Concurrency before that time runs the risk of hidden crashes if an SDK method has dynamic concurrency behaviour as .requestRecordPermission does -- and developers taking that risk regardless should be aware that the documentation may not be a reliable source for determining whether that behaviour exists.

I haven't tested them, but I would assume the compiler-generated await x.requestRecordPermission() functions are also inherently broken, and I'm not aware of any workaround to save them similar to the @Sendable closure trick.

Can you confirm whether the upcoming Xcode 16.3 release will include this fix? A release candidate was posted a few days ago, and I searched the release notes but could not find any reference to this issue.

If not, I hope it can be released very quickly after that.

1 Like

This change happened too late to make the Xcode 16.3 release. You will probably have to wait until the release that includes Swift 6.2, whenever that happens.

When the compiler synthesizes completion handlers for ObjC async calls, it always uses sendable code patterns.

1 Like