Swift 6 Concurrency Errors with MKLocalSearchCompleterDelegate results

Has anyone found a thread-safe pattern that can extract results from completerDidUpdateResults(MKLocalSearchCompleter) in the MKLocalSearchCompleterDelegate ?

I've downloaded the code sample from Interacting with nearby points of interest and notice the conformance throws multiple errors in Xcode 16 Beta 5 with Swift 6:

extension SearchDataSource: MKLocalSearchCompleterDelegate {

    nonisolated func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        Task {
            let suggestedCompletions = completer.results
            await resultStreamContinuation?.yield(suggestedCompletions)
        }
    }

Error: Task-isolated value of type '() async -> ()' passed as a strongly transferred parameter; later accesses could race

and

Error: Sending 'suggestedCompletions' risks causing data races

Is there another technique I can use to share state of suggestedCompletions outside of the delegate in the code sample?

Answered by DTS Engineer in 799526022

Thank you for bringing that to our attention. Those compiler warnings in that sample are new to a recent Xcode beta, as I'm sure they weren't present in the earlier betas of Xcode 16. I'll see about getting that project updated so it compiles again.

In the mean time, you can bind the Task to the main actor and remove the await keyword, like this:

nonisolated func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        Task { @MainActor in
            let suggestedCompletions = completer.results
            resultStreamContinuation?.yield(suggestedCompletions)
        }
    }

With the way this sample project is set up, search completions are very tied to the app UI, so you generally want to ensure that the completer code is associated with the main actor.

—Ed Ford,  DTS Engineer

Thank you for bringing that to our attention. Those compiler warnings in that sample are new to a recent Xcode beta, as I'm sure they weren't present in the earlier betas of Xcode 16. I'll see about getting that project updated so it compiles again.

In the mean time, you can bind the Task to the main actor and remove the await keyword, like this:

nonisolated func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        Task { @MainActor in
            let suggestedCompletions = completer.results
            resultStreamContinuation?.yield(suggestedCompletions)
        }
    }

With the way this sample project is set up, search completions are very tied to the app UI, so you generally want to ensure that the completer code is associated with the main actor.

—Ed Ford,  DTS Engineer

Thank you, @DTS Engineer. I would also expect this approach to work, but the compiler disagrees on two points:

Task { @MainActor in
    let suggestedCompletions = completer.results
    // (with or without the .yield()
}

Error: Task or actor isolated value cannot be sent

and

Task { @MainActor in
...
resultStreamContinuation?.yield(suggestedCompletions)
}

Error: Sending 'suggestedCompletions' risks causing data races

Main actor-isolated 'suggestedCompletions' is passed as a 'sending' parameter; Uses in callee may race with later main actor-isolated uses

(for my own understanding): Is the error suggesting the completer.results reference may have changed by the time the Task { ... } is scheduled/executed?

Swift 6 Concurrency Errors with MKLocalSearchCompleterDelegate results
 
 
Q