NotificationCenter.default.messages() & AsyncAlgorithms collide

I'm trying to compose AsyncSequence of AsyncMessage using functionality from the AsyncAlgorithms package. However, this seems not supported because most of the algorithms in AsyncAlgorithms require input sequences to be Sendable (e.g. combineLatest). NotificationCenter.default.messages return some AsyncSequence<AsyncMessage, Never> which does not conform to Sendable.

Will this issue be addressed in AsyncAlgorithms 1.1, or have I overlooked something?

Here is some examples of code that illustrates the problem and does not compile:

import Foundation
import AsyncAlgorithms

struct MyMessage: NotificationCenter.AsyncMessage {
  typealias Subject = AnyObject
}

@available(macOS 26.0, *)
func myFunc() async {
  let trigger = AsyncStream(unfolding: { true })

  let data = NotificationCenter.default.messages(
    for: MyMessage.self
  ).compactMap { message -> Data? in
    return Data()
  }

  let stream = combineLatest(
    data,
    trigger.removeDuplicates { _,_ in true }
  )

  for await (e, _) in stream {
  }
}
1 Like

The underlying type behind the opaque NotificationCenter.messages() return type is Sendable.

I have a PR up to address this: NotificationCenter.messages() should return Sendable type by cthielen · Pull Request #1473 · swiftlang/swift-foundation · GitHub

4 Likes

That’s an even better solution, of course. Thanks!

Is it possible to incorporate that change into Foundation 6.2 or 6.2.1 for Xcode, enabling its use in iOS applications?

One way this can be fixed, as a work-around, is wrapping it in a custom structure marked as @unchecked Sendable and forward the conformance to the generic:

struct UncheckedSendable<Contents>: @unchecked Sendable {
  var contents: Contents
}

extension UncheckedSendable: AsyncSequence where Contents: AsyncSequence {
  func makeAsyncIterator() -> Contents.AsyncIterator {
    contents.makeAsyncIterator()
  }
}

This is not generally safe btw; in this case however it is because the underlying implementation is safe... but do be careful with this type of sharp tool - it can break things easily.

1 Like