Skip to content

Instantly share code, notes, and snippets.

@oozoofrog
Last active December 24, 2022 13:02
Show Gist options
  • Save oozoofrog/3ac588023ead4ee90f7b73e97d1fc55e to your computer and use it in GitHub Desktop.
Save oozoofrog/3ac588023ead4ee90f7b73e97d1fc55e to your computer and use it in GitHub Desktop.
disposable notification
public extension NotificationCenter {
private class FirstReceiver: NSObject {
let notificationName: Notification.Name
init(_ name: Notification.Name) {
notificationName = name
super.init()
}
var continuation: CheckedContinuation<[AnyHashable: Any]?, Error>?
func setReceive(_ continuation: CheckedContinuation<[AnyHashable: Any]?, Error>) {
self.continuation = continuation
NotificationCenter.default.addObserver(self, selector: #selector(receiveUserInfo), name: notificationName, object: nil)
}
@objc
func receiveUserInfo(_ notification: Notification) {
let continuation = self.continuation
self.continuation = nil
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil)
continuation?.resume(returning: notification.userInfo)
}
func cancel() {
continuation?.resume(throwing: CancellationError())
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil)
}
}
@discardableResult
func receiveFirst(forName name: Notification.Name, timeout: TimeInterval = 0) async throws -> [AnyHashable: Any]? {
if #available(iOS 15, *) {
return try await withThrowingTaskGroup(of: [AnyHashable: Any]?.self, body: { group in
group.addTask {
let notifications = NotificationCenter.default.notifications(named: name)
for await notification in notifications {
return notification.userInfo
}
try Task.checkCancellation()
return nil
}
if timeout > 0 {
group.addTask {
try await Task.sleep(nanoseconds: UInt64(timeout * Double(1_000_000_000)))
throw CancellationError()
}
}
do {
for try await value in group {
return value
}
return nil
} catch {
throw error
}
})
} else {
let receiver = FirstReceiver(name)
return try await withThrowingTaskGroup(of: [AnyHashable: Any]?.self, body: { group in
group.addTask {
try await withTaskCancellationHandler(operation: {
try await withCheckedThrowingContinuation({ continuation in
receiver.setReceive(continuation)
})
}, onCancel: {
receiver.cancel()
})
}
if timeout > 0 {
group.addTask { [weak receiver] in
try await Task.sleep(nanoseconds: UInt64(timeout * Double(1_000_000_000)))
receiver?.cancel()
throw CancellationError()
}
}
for try await value in group {
return value
}
return nil
})
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment