The following simple bidirectional XPC crashes on Swift Language Version 6 but it happily compiles and successfully run on 5.
PS: Initially, I was writing this on the Apple Forums, but I decided to put it here instead since it seems to be related to Swift concurrency changes and not a specific Apple bug.
import SwiftUI
// The macOS app
@main struct MyApp: App {
var body: some Scene {
WindowGroup {
VStack {
Image(systemName: "globe")
}.task {
let session = try! XPCSession(xpcService: "me.dehesa.swift.xpc")
let reply: String = try! session.sendSync("Hello Service! I'm app")
session.cancel(reason: "Done messaging")
}
}
}
}
// The XPC service
@main final class Service {
static func main() {
_ = try! XPCListener(service: "me.dehesa.swift.xpc") { request in
request.accept { (message: String) in "Hello app! I am the XPC service" }
}
dispatchMain()
}
}
Reading SE-0281, SE-0323, and other Swift forum posts such as this one; it seems that the compiler now preemptively crashes rather than silently ignore when preconditions are not held (as @ktoso puts it). However, it is hard for me to figure out what the problem is here. My guess is that you cannot call dispatchMain() from code already in the MainActor (which the static func main implicitly is).
XPC has not been properly updated to work with Swift concurrency. This is a long known issue that results in crashes like this, so it's ultimately an Apple issue, unfortunately. I'll leave it to others to try and help your work around it.
In the XPCListener documentation, the incomingSessionHandler closure is not marked Sendable. This means the compiler expects it will not cross isolation boundaries -- this has been widely regarded as suboptimal behavior, and it's more of an artifact of this code being imported from Objective-C. This is called out in SE-0463 ( Import Objective-C completion handler parameters as @Sendable).
As a workaround for now, you can mark your implementation of incomingSessionHandler as requiring an @Sendable closure, which expresses to the concurrency system that the closure is expected to cross isolation boundaries.
// The XPC service
@main final class Service {
static func main() {
_ = try! XPCListener(service: "me.dehesa.swift.xpc") { @Sendable request in
request.accept { @Sendable (message: String) in
"Hello app! I am the XPC service"
}
}
dispatchMain()
}
}
XPC has not been properly updated for Swift concurrency, and
incomingSessionHandler isn't properly marked as Sendable.
It is not the problem here. The code seems to crash when it reaches dispatchMain() (marking the closure @Sendable, as @harlanhaskins suggests, doesn't fix the crash although it makes it semantically correct).
Interestingly, the code successfully run by extracting the static main function out to a global free function like so:
@main final class Service {
static func main() {
startListening()
}
}
private func startListening() {
_ = try! XPCListener(service: "me.dehesa.swift.xpc") { request in
request.accept { (message: String) in
"Hello app! I am the XPC service"
}
}
dispatchMain()
}
The code also runs well if I decide to use a main.swift file instead of the @main syntax. Therefore, I am inclined to believe the bug is related to the rules specified in SE-0323 mixed with Swift Language Version 6 behavior.
I owe you an apology @harlanhaskins. Indeed when I mark both closures with @Sendable, the XPC service runs successfully. I just marked one of them before writing the comment above.