You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# In the first period after branching the release branch, we typically want to include many changes from `main` in the release branch. This workflow automatically creates a PR every Monday to merge main into the release branch.
3
+
# Later in the release cycle we should stop this practice to avoid landing risky changes by disabling this workflow. To do so, disable the workflow as described in https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow
if: (github.event_name == 'schedule' && github.repository == 'swiftlang/swift-foundation') || (github.event_name != 'schedule') # Ensure that we don't run this on a schedule in a fork
***v2** Remove `static` from `NotificationCenter.Message.isolation` to better support actor instances
12
12
***v3** Remove generic isolation pattern in favor of dedicated `MainActorMessage` and `AsyncMessage` types. Apply SE-0299-style static member lookups for `addObserver()`. Provide default value for `Message.name`.
13
+
***v4** Add `AsyncSequence` APIs for observing. Expand `Message.Subject` conformance to take either `AnyObject` or `Identifiable` where `Identifiable.ID == ObjectIdentifier`. Document `ObservationToken` automatic de-registration behavior. Drop `with` label on `post()` methods in favor of `subject` for clarity.
@@ -194,32 +195,51 @@ The protocol specifies `makeMessage(:Notification)` and `makeNotification(:Self)
194
195
195
196
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.
196
197
198
+
Each `Message` specifies a specific *subject* variable or metatype to observe, similar to the existing `Notification.object`, e.g. an `NSWindow` instance or the `NSWindow.self` metatype. `Message.Subject` has no conformance requirements in its protocol, but `addObserver()` and `post()` both refine `Message.Subject` to either conform to `AnyObject` or confirm to `Identifiable` where `Identifiable.ID == ObjectIdentifier`.
199
+
197
200
### Observing messages
198
201
199
202
Observing messages can be done with new overloads to `addObserver`. Clients do not need to know whether a message conforms to `MainActorMessage` or `AsyncMessage`.
200
203
204
+
Overloads are provided both for `Message.Subject: AnyObject` and `Message.Subject: Identifiable where ID == ObjectIdentifier`. This allows the observation of both reference types and value types which can provide an `ObjectIdentifier`.
205
+
201
206
For `MainActorMessage`:
202
207
203
208
```swift
204
209
@available(FoundationPreview 0.5, *)
205
210
extensionNotificationCenter {
206
211
// e.g. addObserver(of: workspace, for: .willLaunchApplication) { message in ... }
When an `ObservationToken` goes out of scope, the corresponding observer will be removed from its center automatically if it is still registered. This behavior helps prevent memory leaks from tokens which are accidentally dropped by the user.
297
+
298
+
Messages conforming to `AsyncMessage` can also be observed using a set of `AsyncSequence`-conforming APIs, similar to the existing `notifications(named:object:)` method:
) ->some AsyncSequence<Message, Never> where Identifier.MessageType == Message
320
+
321
+
publicfuncmessages<Message: AsyncMessage>(
322
+
ofsubject: Message.Subject?=nil,
323
+
formessageType: Message.Type,
324
+
bufferSizelimit: Int=10
325
+
) ->some AsyncSequence<Message, Never> where Message.Subject:AnyObject
326
+
327
+
publicfuncmessages<Message: AsyncMessage>(
328
+
ofsubject: Message.Subject?=nil,
329
+
formessageType: Message.Type,
330
+
bufferSizelimit: Int=10
331
+
) ->some AsyncSequence<Message, Never> where Message.Subject:Identifiable, Message.Subject.ID ==ObjectIdentifier
332
+
}
333
+
```
334
+
335
+
These allow for the familiar `for await in` syntax:
336
+
337
+
```swift
338
+
forawait message in center.messages(of: anObject, for: .anAsyncMessage) {
339
+
// ...
340
+
}
341
+
342
+
forawait message in center.messages(for: AnAsyncMessage.self) {
343
+
// ...
344
+
}
345
+
346
+
// etc.
347
+
```
348
+
349
+
The `messages()` sequence uses a reasonably-sized buffer to reduce the likelihood of dropped messages caused by the interaction of synchronous and asynchronous code. When a `Message` is dropped, the implementation will log to aid in debugging. Message frequency in practice is typically 0-2x / second / message type and therefore unlikely to result in dropped messages. Certain UI-related messages can post in practice as often as 40 - 50x / second / message, but these are typically `MainActorMessage` and would not be subject to dropping nor available for use with `messages()`.
350
+
261
351
### Posting messages
262
352
263
353
Posting messages can be done with new overloads on the existing `post` method:
264
354
265
355
```swift
266
356
@available(FoundationPreview 0.5, *)
267
357
extensionNotificationCenter {
268
-
publicfuncpost<M: Message>(_message: M, withsubject: M.Subject)
269
-
publicfuncpost<M: Message>(_message: M, withsubject: M.Subject.Type)
However, not all messages have subject instances (e.g. `addObserver(of: NSWindow.self, for: .willMove)`). While `post()` could take a default parameter for an optional `subject`, the `addObserver()` closure would always have to specify a `subject` parameter even for messages without subject instances.
0 commit comments