Don't restrict anything to the main actor

This commit is contained in:
Sami Samhuri 2025-04-26 11:05:19 -07:00
parent 9a7fbdf09d
commit b8eb878097
No known key found for this signature in database
4 changed files with 13 additions and 13 deletions

View file

@ -31,7 +31,7 @@ When you're integrating this into an app with Xcode then go to your project's Pa
When you're integrating this using SPM on its own then add this to the list of dependencies your Package.swift file: When you're integrating this using SPM on its own then add this to the list of dependencies your Package.swift file:
```swift ```swift
.package(url: "https://github.com/samsonjs/AsyncMonitor.git", .upToNextMajor(from: "0.1.0")) .package(url: "https://github.com/samsonjs/AsyncMonitor.git", .upToNextMajor(from: "0.1.1"))
``` ```
and then add `"AsyncMonitor"` to the list of dependencies in your target as well. and then add `"AsyncMonitor"` to the list of dependencies in your target as well.
@ -43,7 +43,7 @@ The simplest example uses a closure that receives the notification:
```swift ```swift
import AsyncMonitor import AsyncMonitor
@MainActor class SimplestVersion { class SimplestVersion {
let cancellable = NotificationCenter.default let cancellable = NotificationCenter.default
.notifications(named: .NSCalendarDayChanged).map(\.name) .notifications(named: .NSCalendarDayChanged).map(\.name)
.monitor { _ in .monitor { _ in
@ -57,7 +57,7 @@ This example uses the context parameter to avoid reference cycles with `self`:
```swift ```swift
import AsyncMonitor import AsyncMonitor
@MainActor class WithContext { class WithContext {
var cancellables = Set<AnyAsyncCancellable>() var cancellables = Set<AnyAsyncCancellable>()
init() { init() {

View file

@ -6,13 +6,13 @@ public extension NSObjectProtocol where Self: NSObject {
func values<Value: Sendable>( func values<Value: Sendable>(
for keyPath: KeyPath<Self, Value>, for keyPath: KeyPath<Self, Value>,
options: NSKeyValueObservingOptions = [], options: NSKeyValueObservingOptions = [],
changeHandler: @escaping @MainActor (Value) -> Void changeHandler: @escaping (Value) -> Void
) -> any AsyncCancellable { ) -> any AsyncCancellable {
let (stream, continuation) = AsyncStream<Value>.makeStream() let (stream, continuation) = AsyncStream<Value>.makeStream()
let token = self.observe(keyPath, options: options) { object, _ in let token = self.observe(keyPath, options: options) { object, _ in
continuation.yield(object[keyPath: keyPath]) continuation.yield(object[keyPath: keyPath])
} }
return stream.monitor { @MainActor value in return stream.monitor { value in
_ = token // keep this alive _ = token // keep this alive
changeHandler(value) changeHandler(value)
} }

View file

@ -2,7 +2,7 @@ import Foundation
import Testing import Testing
@testable import AsyncMonitor @testable import AsyncMonitor
@MainActor class AsyncMonitorTests { class AsyncMonitorTests {
let center = NotificationCenter() let center = NotificationCenter()
let name = Notification.Name("a random notification") let name = Notification.Name("a random notification")
@ -24,18 +24,18 @@ import Testing
subject = center.notifications(named: name).map(\.name).monitor { receivedName in subject = center.notifications(named: name).map(\.name).monitor { receivedName in
Issue.record("Called for irrelevant notification \(receivedName)") Issue.record("Called for irrelevant notification \(receivedName)")
} }
Task { Task { [center] in
center.post(name: Notification.Name("something else"), object: nil) center.post(name: Notification.Name("something else"), object: nil)
} }
try await Task.sleep(for: .milliseconds(10)) try await Task.sleep(for: .milliseconds(10))
} }
@Test func stopsCallingBlockWhenDeallocated() async throws { @Test @MainActor func stopsCallingBlockWhenDeallocated() async throws {
subject = center.notifications(named: name).map(\.name).monitor { _ in subject = center.notifications(named: name).map(\.name).monitor { _ in
Issue.record("Called after deallocation") Issue.record("Called after deallocation")
} }
Task { Task { @MainActor in
subject = nil subject = nil
center.post(name: name, object: nil) center.post(name: name, object: nil)
} }
@ -48,7 +48,7 @@ import Testing
private var cancellable: (any AsyncCancellable)? private var cancellable: (any AsyncCancellable)?
@MainActor init(center: NotificationCenter, deinitHook: @escaping () -> Void) { init(center: NotificationCenter, deinitHook: @escaping () -> Void) {
self.deinitHook = deinitHook self.deinitHook = deinitHook
let name = Notification.Name("irrelevant name") let name = Notification.Name("irrelevant name")
cancellable = center.notifications(named: name).map(\.name) cancellable = center.notifications(named: name).map(\.name)
@ -78,7 +78,7 @@ import Testing
Issue.record("Called after context was deallocated") Issue.record("Called after context was deallocated")
} }
context = nil context = nil
Task { Task { [center, name] in
center.post(name: name, object: nil) center.post(name: name, object: nil)
} }
try await Task.sleep(for: .milliseconds(10)) try await Task.sleep(for: .milliseconds(10))

View file

@ -1,7 +1,7 @@
import Foundation import Foundation
@testable import AsyncMonitor @testable import AsyncMonitor
@MainActor class SimplestVersion { class SimplestVersion {
let cancellable = NotificationCenter.default let cancellable = NotificationCenter.default
.notifications(named: .NSCalendarDayChanged).map(\.name) .notifications(named: .NSCalendarDayChanged).map(\.name)
.monitor { _ in .monitor { _ in
@ -9,7 +9,7 @@ import Foundation
} }
} }
@MainActor class WithContext { class WithContext {
var cancellables = Set<AnyAsyncCancellable>() var cancellables = Set<AnyAsyncCancellable>()
init() { init() {