Contents

NotificationCenter.AsyncMessage

A protocol for creating types that you can post to a notification center, which posts them to an arbitrary isolation.

Declaration

protocol AsyncMessage : Sendable

Overview

You post types conforming to AsyncMessage to a notification center using post(_:subject:) and observe them with addObserver(of:for:using:).

The notification center delivers AsyncMessage types asynchronously when posted. Asynchronous delivery isn’t suitable for messages with time-critical deliveries, such as a message that must have its observers called before a certain action takes place.

For types that post on the main actor, use NotificationCenter.MainActorMessage.

Each AsyncMessage is associated with a specific Subject type.

For example, an AsyncMessage associated with the type Event could use the following declaration:

struct EventDidStart: NotificationCenter.AsyncMessage {
    typealias Subject = Event
}

AsyncMessage can use an optional NotificationCenter.MessageIdentifier type for context-aware observer registration:

extension NotificationCenter.MessageIdentifier where Self == NotificationCenter.BaseMessageIdentifier<EventDidStart> {
    static var didStart: Self { .init() }
}

With this identifier, observers can receive information about a specific instance by registering for this message with a NotificationCenter:

let observerToken = NotificationCenter.default.addObserver(of: importantEvent, for: .didStart)

Or an observer can receive information about any instance with:

let observerToken = NotificationCenter.default.addObserver(of: Event.self, for: .didStart)

The notification center ties observation the lifetime of the returned NotificationCenter.ObservationToken and automatically de-registers the observer if the token goes out of scope. You can also remove observation explicitly:

NotificationCenter.default.removeObserver(observerToken)

Notification Interoperability

AsyncMessage includes optional interoperability with Notification, enabling posters and observers of both types to pass information.

It does this by offering a makeMessage(_:) method that collects values from a Notification‘s userInfo and populates properties on a new message. In the other direction, a makeNotification(_:) method collects the message’s defined properties and loads them into a new notification’s userInfo dictionary.

For example, if there exists a Notification posted on an arbitrary isolation identified by the Notification.Name "eventDidFinish" with a userInfo dictionary containing the key "duration" as an NSNumber, an app could post and observe the notification with the following NotificationCenter.AsyncMessage:

struct EventDidFinish: NotificationCenter.AsyncMessage {
    typealias Subject = Event
    static var name: Notification.Name { Notification.Name("eventDidFinish") }

    var duration: Int

    static func makeNotification(_ message: Self) -> Notification {
        return Notification(name: Self.name, userInfo: ["duration": NSNumber(message.duration)])
    }

    static func makeMessage(_ notification: Notification) -> Self? {
        guard let userInfo = notification.userInfo,
              let duration = userInfo["duration"] as? Int
        else {
            return nil
        }

        return Self(duration: duration)
    }
}

With this definition, an observer for this AsyncMessage type receives information even if the poster used the Notification equivalent, and vice versa.

Topics

Declaring the message name and subject

Converting between messages and notifications

See Also

Declaring a message