Skip to content

Commit 832f750

Browse files
committed
Refine format of concurrency-safe-notifications.md
1 parent eeb7ba3 commit 832f750

File tree

1 file changed

+52
-44
lines changed

1 file changed

+52
-44
lines changed

Proposals/0011-concurrency-safe-notifications.md

+52-44
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ The optional lookup type, `NotificationCenter.MessageIdentifier`, provides an [S
7474

7575
The first parameter of `addObserver(of:for:)` accepts both metatypes and instance types. Registering with a metatype enables an observer to receive all messages for the given identifier (equivalent to `object = nil` in the current `NotificationCenter`), while registering with an instance will only deliver messages related to that instance.
7676

77-
`NotificationCenter.Message` provides optional bi-directional interoperability with the existing `Notification` type by using the `Notification.Name` property and two optional methods, `makeMessage(:Notification)` and `makeNotification(:Self)`:
77+
`NotificationCenter.Message` provides optional bi-directional interoperability with the existing `Notification` type by using the `Notification.Name` property and two optional methods, `makeMessage(_:)` and `makeNotification(_:)`:
7878

7979
```swift
8080
// Framework-side
@@ -89,7 +89,7 @@ extension NSWorkspace {
8989

9090
public static func makeMessage(_ notification: Notification) -> Self? {
9191
guard
92-
let application = notification.userInfo?["applicationUserInfoKey"] as? NSRunningApplication
92+
let application = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication
9393
else {
9494
return nil
9595
}
@@ -99,8 +99,7 @@ extension NSWorkspace {
9999

100100
// makeNotification() does not need to translate `object`
101101
public static func makeNotification(_ message: Self) -> Notification {
102-
return Notification(name: Self.name,
103-
userInfo: ["applicationUserInfoKey": message.application])
102+
return Notification(name: Self.name, userInfo: [NSWorkspace.applicationUserInfoKey: message.application])
104103
}
105104
}
106105
}
@@ -110,7 +109,7 @@ Using these methods, posters and observers of both the `Notification` and `Messa
110109

111110
## Example usage
112111

113-
This example adapts the existing [NSWorkspace.willLaunchApplicationNotification](https://developer.apple.com/documentation/appkit/nsworkspace/1528611-willlaunchapplicationnotificatio) `Notification` to use `NotificationCenter.Message`. It defines the optional `MessageIdentifier` to make registering observers easier, and it defines `makeMessage(:Notification)` and `makeNotification(:Self)` for bi-directional interoperability with existing NotificationCenter posters and observers.
112+
This example adapts the existing [NSWorkspace.willLaunchApplicationNotification](https://developer.apple.com/documentation/appkit/nsworkspace/1528611-willlaunchapplicationnotificatio) `Notification` to use `NotificationCenter.Message`. It defines the optional `MessageIdentifier` to make registering observers easier, and it defines `makeMessage(_:)` and `makeNotification(_:)` for bi-directional interoperability with existing NotificationCenter posters and observers.
114113

115114
Existing code which vends notifications do not need to alter existing `Notification` declarations, observers, or posts to adopt to this proposal.
116115

@@ -124,7 +123,7 @@ extension NSWorkspace {
124123

125124
public static func makeMessage(_ notification: Notification) -> Self? {
126125
guard
127-
let application = notification.userInfo?["applicationUserInfoKey"] as? NSRunningApplication
126+
let application = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication
128127
else {
129128
return nil
130129
}
@@ -133,12 +132,13 @@ extension NSWorkspace {
133132
}
134133

135134
public static func makeNotification(_ message: Self) -> Notification {
136-
return Notification(name: Self.name, userInfo: ["applicationUserInfoKey": message.application])
135+
return Notification(name: Self.name, userInfo: [NSWorkspace.applicationUserInfoKey: message.application])
137136
}
138137
}
139138
}
140139

141-
extension NotificationCenter.MessageIdentifier where Self == NotificationCenter.BaseMessageIdentifier<NSWorkspace.WillLaunchApplication> {
140+
extension NotificationCenter.MessageIdentifier
141+
where Self == NotificationCenter.BaseMessageIdentifier<NSWorkspace.WillLaunchApplication> {
142142
static var willLaunchApplication: Self { .init() }
143143
}
144144
```
@@ -190,7 +190,7 @@ extension NotificationCenter {
190190

191191
`NotificationCenter.Message` is designed to interoperate with existing uses of `Notification` by sharing `Notification.Name` identifiers. This means an observer expecting `NotificationCenter.Message` will be called when a `Notification` is posted if the `Notification.Name` identifier matches, and vice versa.
192192

193-
The protocol specifies `makeMessage(:Notification)` and `makeNotification(:Self)` to transform the payload between posters and observers of both the `NotificationCenter.Message` and `Notification` types. These methods have default implementations in cases where interoperability with `Notification` is not necessary.
193+
The protocol specifies `makeMessage(_:)` and `makeNotification(_:)` to transform the payload between posters and observers of both the `NotificationCenter.Message` and `Notification` types. These methods have default implementations in cases where interoperability with `Notification` is not necessary.
194194

195195
For `Message` types that do not need to interoperate with existing `Notification` uses, the `name` property does not need to be specified, and will default to the fully qualified name of the `Message` type, e.g. `MyModule.MyMessage`. Note that when using this default, renaming the type or relocating it to another module has a similar effect as changing ABI, as any code that was compiled separately will not be aware of the name change until recompiled. Developers can control this effect by explicitly setting the `name` property if needed.
196196

@@ -204,22 +204,25 @@ For `MainActorMessage`:
204204
@available(FoundationPreview 0.5, *)
205205
extension NotificationCenter {
206206
// e.g. addObserver(of: workspace, for: .willLaunchApplication) { message in ... }
207-
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(of subject: M.Subject,
208-
for identifier: I,
209-
using observer: @escaping @MainActor (M) -> Void)
210-
-> ObservationToken where I.MessageType == M
207+
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(
208+
of subject: M.Subject,
209+
for identifier: I,
210+
using observer: @escaping @MainActor (M) -> Void
211+
) -> ObservationToken where I.MessageType == M
211212

212213
// e.g. addObserver(of: NSWorkspace.self, for: .willLaunchApplication) { message in ... }
213-
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(of subject: M.Subject.Type,
214-
for identifier: I,
215-
using observer: @escaping @MainActor (M) -> Void)
216-
-> ObservationToken where I.MessageType == M
214+
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(
215+
of subject: M.Subject.Type,
216+
for identifier: I,
217+
using observer: @escaping @MainActor (M) -> Void
218+
) -> ObservationToken where I.MessageType == M
217219

218220
// e.g. addObserver(NSWorkspace.WillLaunchApplication.self) { message in ... }
219-
public func addObserver<M: MainActorMessage>(_ messageType: M.Type,
220-
subject: M.Subject? = nil,
221-
using observer: @escaping @MainActor (M) -> Void)
222-
-> ObservationToken
221+
public func addObserver<M: MainActorMessage>(
222+
_ messageType: M.Type,
223+
subject: M.Subject? = nil,
224+
using observer: @escaping @MainActor (M) -> Void
225+
) -> ObservationToken
223226
}
224227
```
225228

@@ -228,20 +231,23 @@ And for `AsyncMessage`:
228231
```swift
229232
@available(FoundationPreview 0.5, *)
230233
extension NotificationCenter {
231-
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(of subject: M.Subject,
232-
for identifier: I,
233-
using observer: @escaping @Sendable (M) async -> Void)
234-
-> ObservationToken where I.MessageType == M
235-
236-
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(of subject: M.Subject.Type,
237-
for identifier: I,
238-
using observer: @escaping @Sendable (M) async -> Void)
239-
-> ObservationToken where I.MessageType == M
234+
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(
235+
of subject: M.Subject,
236+
for identifier: I,
237+
using observer: @escaping @Sendable (M) async -> Void
238+
) -> ObservationToken where I.MessageType == M
239+
240+
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(
241+
of subject: M.Subject.Type,
242+
for identifier: I,
243+
using observer: @escaping @Sendable (M) async -> Void
244+
) -> ObservationToken where I.MessageType == M
240245

241-
public func addObserver<M: AsyncMessage>(_ messageType: M.Type,
242-
subject: M.Subject? = nil,
243-
using observer: @escaping @Sendable (M) async -> Void)
244-
-> ObservationToken
246+
public func addObserver<M: AsyncMessage>(
247+
_ messageType: M.Type,
248+
subject: M.Subject? = nil,
249+
using observer: @escaping @Sendable (M) async -> Void
250+
) -> ObservationToken
245251
}
246252
```
247253

@@ -276,7 +282,7 @@ While both `post()` methods are called synchronously, only the `MainActorMessage
276282

277283
### Interoperability with `Notification`
278284

279-
Clients can migrate information to and from existing `Notification` types using `NotificationCenter.Message.makeMessage(:Notification)` and `NotificationCenter.Message.makeNotification(:Self)`. Implementing these enables the mixing of posters and observers between the `Notification` and `NotificationCenter.Message` types:
285+
Clients can migrate information to and from existing `Notification` types using `NotificationCenter.Message.makeMessage(_:)` and `NotificationCenter.Message.makeNotification(_:)`. Implementing these enables the mixing of posters and observers between the `Notification` and `NotificationCenter.Message` types:
280286

281287
```swift
282288
struct EventDidOccur: NotificationCenter.Message {
@@ -296,20 +302,20 @@ struct EventDidOccur: NotificationCenter.Message {
296302

297303
These methods do not need to be implemented if all posters and observers are using `NotificationCenter.Message`.
298304

299-
See the table below for the effects of implementing `makeMessage(:Notification)` / `makeNotification(:Self)`:
305+
See the table below for the effects of implementing `makeMessage(_:)` / `makeNotification(_:)`:
300306

301307
| Posting... | Observing... | Behavior |
302308
| ------------- | --------------- | ------------------------------------------ |
303-
| Message | Notification | Notification observers will receive the result of `makeNotification(:Self)` if available, else they will be called with a `nil` value for `userInfo` |
304-
| Notification | Message | Message observers will receive the result of `makeMessage(:Notification)` if available, else the observer will not be called |
309+
| Message | Notification | Notification observers will receive the result of `makeNotification(_:)` if available, else they will be called with a `nil` value for `userInfo` |
310+
| Notification | Message | Message observers will receive the result of `makeMessage(_:)` if available, else the observer will not be called |
305311

306312
### Isolation from non-Swift Concurrency posters
307313

308314
Observers called via the existing, pre-Swift Concurrency `.post()` methods are either called on the same thread as the poster, or called in an explicitly passed `OperationQueue`.
309315

310316
However, users can still adopt `NotificationCenter.Message` with pre-Swift Concurrency `.post()` calls by providing a `NotificationCenter.Message` with the proper `Notification.Name` value and picking the correct type between `MainActorMessage` and `AsyncMessage`.
311317

312-
For example, if an Objective-C method calls the `post(name:object:userInfo:)` method on the main thread, `NotificationCenter.MainActorMessage` can be used to define a message with the same `Notification.Name`, enabling clients observing the message to access the `object` and `userInfo` parameters of the original `Notification` in a safe manner through `makeMessage(:Notification)`.
318+
For example, if an Objective-C method calls the `post(name:object:userInfo:)` method on the main thread, `NotificationCenter.MainActorMessage` can be used to define a message with the same `Notification.Name`, enabling clients observing the message to access the `object` and `userInfo` parameters of the original `Notification` in a safe manner through `makeMessage(_:)`.
313319

314320
## Impact on existing code
315321

@@ -328,12 +334,14 @@ A previous iteration of this proposal stored an `Actor`-conforming type on the `
328334

329335
```swift
330336
public func addObserver<MessageType: NotificationCenter.Message>(
331-
_ notification: MessageType.Type,
332-
observer: @Sendable @escaping (MessageType, isolated MessageType.Isolation) -> Void
333-
) -> ObservationToken
337+
_ notification: MessageType.Type,
338+
observer: @Sendable @escaping (MessageType, isolated MessageType.Isolation) -> Void
339+
) -> ObservationToken
334340

335-
public func post<MessageType: NotificationCenter.Message>(_ message: MessageType,
336-
isolation: isolated MessageType.Isolation)
341+
public func post<MessageType: NotificationCenter.Message>(
342+
_ message: MessageType,
343+
isolation: isolated MessageType.Isolation
344+
)
337345
```
338346

339347
Unfortunately, the design required careful handling to use correctly and had some ergonomic shortcomings:

0 commit comments

Comments
 (0)