-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Description
I found a consistent crash due to the Swift 6 strict concurrency checker lacking to detect sink
usage in a @MainActor
isolated context.


Reproduction
Consider the following code:
@MainActor
final class NotificationObserver {
private(set) var didCallNotification = false
private var cancellables: [AnyCancellable] = []
init() {
/// 💥 Observing notifications using a selector results in the same crash:
NotificationCenter.default.addObserver(self, selector: #selector(selectorNotificationCalled), name: .notificationA, object: nil)
/// 💥 Observing notifications using Combine publisher results in a crash:
/// EXC_BREAKPOINT
NotificationCenter.default.publisher(for: .notificationA)
.sink { [weak self] _ in
self?.didReceiveConcurrentNotification()
}.store(in: &cancellables)
}
@objc func selectorNotificationCalled() {
didReceiveConcurrentNotification()
}
private func didReceiveConcurrentNotification() {
didCallNotification = true
}
}
Posting the notification from a nonisolated or @MainActor
isolation domain all works fine. However, calling from a different isolation context will cause a crash:
struct DetachedNotificationPoster {
func post() async {
await Task.detached {
NotificationCenter.default.post(name: .notificationA, object: nil)
}.value
}
}
I'm using a detached task here to simplify reproduction.
Here's a test in case that helps:
@MainActor
@Test func detachedTaskPost() async throws {
let observer = NotificationObserver()
let poster = DetachedNotificationPoster()
await poster.post()
#expect(observer.didCallNotification)
}
Stack dump
0x18e34e55c <+76>: adrp x1, 59
0x18e34e560 <+80>: add x1, x1, #0x9e5 ; "%sBlock was %sexpected to execute on queue [%s (%p)]"
0x18e34e564 <+84>: sub x0, x29, #0x18
0x18e34e568 <+88>: bl 0x18e3861a8 ; symbol stub for: asprintf
0x18e34e56c <+92>: ldur x19, [x29, #-0x18]
0x18e34e570 <+96>: str x19, [sp]
0x18e34e574 <+100>: adrp x0, 59
0x18e34e578 <+104>: add x0, x0, #0xa50 ; "%s"
0x18e34e57c <+108>: bl 0x18e37eb68 ; _dispatch_log
0x18e34e580 <+112>: adrp x8, 441525
0x18e34e584 <+116>: str x19, [x8, #0x8c0]
-> 0x18e34e588 <+120>: brk #0x1
Expected behavior
I would expect Concurrency Checking to take place, similarly to use e.g. the notification observer with a block:

Environment
swift-driver version: 1.127.10 Apple Swift version 6.2 (swiftlang-6.2.0.14.8 clang-1700.3.14.6)
Target: arm64-apple-macosx15.0
Additional information
This impacts quite some projects that currently migrated to Swift 6 & Strict Concurrency while having Combine publishers. This is an example of notifications, but I would not be surprised if this could also happen to other kind of publishers.