Contents

NotificationCenter.MainActorMessage

A protocol for creating types that you can post to a notification center and bind to the main actor.

Declaration

protocol MainActorMessage : SendableMetatype

Overview

You post types conforming to MainActorMessage to a notification center using post(_:subject:) and observe them with addObserver(of:for:using:). The notification center delivers MainActorMessage types synchronously when posted.

For types that post on an arbitrary isolation, use NotificationCenter.AsyncMessage.

Each MainActorMessage is associated with a specific Subject type.

For example, a MainActorMessage associated with the type Event could use the following declaration:

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

MainActorMessage 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

MainActorMessage 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 MainActor 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.MainActorMessage:

struct EventDidFinish: NotificationCenter.MainActorMessage {
    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 MainActorMessage 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