Skip to content

Instantly share code, notes, and snippets.

@nearfri
Last active June 15, 2023 03:08
Show Gist options
  • Save nearfri/67c43f91230c42d550c5980cd02a5607 to your computer and use it in GitHub Desktop.
Save nearfri/67c43f91230c42d550c5980cd02a5607 to your computer and use it in GitHub Desktop.
Typed Notification
protocol TypedNotification {
static var name: Notification.Name { get }
static func generate(from notification: Notification) -> Self
}
protocol CustomTypedNotification: TypedNotification {}
extension CustomTypedNotification {
static var name: Notification.Name {
return .init(String(reflecting: self))
}
static func generate(from notification: Notification) -> Self {
let notificatonKey = TypedUserInfo.notificationUserInfoKey
guard let result = notification.userInfo?[notificatonKey] as? Self else {
preconditionFailure("\(Self.self) value not found in userInfo")
}
return result
}
}
struct TypedUserInfo {
fileprivate static let notificationUserInfoKey = "TypedNotificationUserInfoKey"
private let userInfo: [AnyHashable: Any]
init(_ userInfo: [AnyHashable: Any]?) {
self.userInfo = userInfo ?? [:]
}
subscript<Value>(key: AnyHashable) -> Value? {
return userInfo[key] as? Value
}
}
extension NotificationCenter {
func addObserver<T: TypedNotification>(
for notification: T.Type,
object: Any? = nil,
queue: OperationQueue? = nil,
using block: @escaping (T) -> Void
) -> NSObjectProtocol {
return addObserver(forName: T.name, object: object, queue: queue) { noti in
let notification = T.generate(from: noti)
block(notification)
}
}
func publisher<T: TypedNotification>(
for notification: T.Type,
object: AnyObject? = nil
) -> AnyPublisher<T, Never> {
return publisher(for: T.name, object: object)
.map(T.generate(from:))
.eraseToAnyPublisher()
}
func post<T: CustomTypedNotification>(_ notification: T, object: AnyObject? = nil) {
let userInfo: [AnyHashable: Any] = [TypedUserInfo.notificationUserInfoKey: notification]
post(name: T.name, object: object, userInfo: userInfo)
}
}
class BaseKeyboardNotification: TypedNotification {
class var name: Notification.Name { preconditionFailure("Must override") }
var isLocal: Bool
var frameBegin: CGRect
var frameEnd: CGRect
var animationDuration: TimeInterval
static func generate(from notification: Notification) -> Self {
return Self.init(notification)
}
required init(_ notification: Notification) {
let userInfo = TypedUserInfo(notification.userInfo)
self.isLocal = userInfo[UIResponder.keyboardIsLocalUserInfoKey] ?? false
self.frameBegin = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] ?? .null
self.frameEnd = userInfo[UIResponder.keyboardFrameEndUserInfoKey] ?? .null
self.animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] ?? 0
}
}
class KeyboardWillShowNotification: BaseKeyboardNotification {
override class var name: Notification.Name { UIResponder.keyboardWillShowNotification }
}
class KeyboardWillHideNotification: BaseKeyboardNotification {
override class var name: Notification.Name { UIResponder.keyboardWillHideNotification }
}
token = nc.addObserver(for: KeyboardWillShowNotification.self) { note in
print(note.animationDuration)
}
nc.publisher(for: KeyboardWillShowNotification.self).sink { note in
print(note.animationDuration)
}
.store(in: &subscriptions)
struct LocalMessageNotification: CustomTypedNotification {
let id: String
var message: String
}
NotificationCenter.default.publisher(for: LocalMessageNotification.self).sink { note in
print("id: \(note.id)")
print("message: \(note.message)")
}
.store(in: &subscriptions)
NotificationCenter.default.post(LocalMessageNotification(id: "1", message: "hello"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment