Skip to content

Instantly share code, notes, and snippets.

@nearfri
Last active July 1, 2024 09:32
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
public protocol CustomTypedNotification: TypedNotification {}
extension CustomTypedNotification {
public static var name: Notification.Name {
return .init(String(reflecting: self))
}
public static func generate(from notification: Notification) -> Self {
let notificationKey = TypedUserInfo.notificationUserInfoKey
guard let result = notification.userInfo?[notificationKey] as? Self else {
preconditionFailure("\(Self.self) value not found in userInfo")
}
return result
}
}
extension TypedUserInfo {
fileprivate static let notificationUserInfoKey = "TypedNotificationUserInfoKey"
}
extension NotificationCenter {
public func post<T: CustomTypedNotification>(_ notification: T, object: AnyObject? = nil) {
let userInfo: [AnyHashable: Any] = [TypedUserInfo.notificationUserInfoKey: notification]
post(name: T.name, object: object, userInfo: userInfo)
}
}
public protocol TypedNotification {
static var name: Notification.Name { get }
static func generate(from notification: Notification) -> Self
}
public struct TypedUserInfo {
private let userInfo: [AnyHashable: Any]
public init(_ userInfo: [AnyHashable: Any]?) {
self.userInfo = userInfo ?? [:]
}
public subscript<Value>(key: AnyHashable) -> Value? {
return userInfo[key] as? Value
}
}
extension NotificationCenter {
public 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) { note in
let notification = T.generate(from: note)
block(notification)
}
}
public 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()
}
}
public class BaseKeyboardNotification: TypedNotification, CustomDebugStringConvertible {
public class var name: Notification.Name { preconditionFailure("Must override") }
public let isLocal: Bool
public let frameBegin: CGRect
public let frameEnd: CGRect
public let animationDuration: TimeInterval
public let animationCurve: UIView.AnimationCurve
public 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
let curveValue: Int = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] ?? 0
self.animationCurve = UIView.AnimationCurve(rawValue: curveValue) ?? .easeInOut
}
public var debugDescription: String {
return """
\(Self.name.rawValue) - frameBegin: \(frameBegin), frameEnd: \(frameEnd), \
duration: \(animationDuration)
"""
}
}
public enum KeyboardNotification {
public class WillShow: BaseKeyboardNotification {
override public class var name: Notification.Name {
UIResponder.keyboardWillShowNotification
}
}
public class DidShow: BaseKeyboardNotification {
override public class var name: Notification.Name {
UIResponder.keyboardDidShowNotification
}
}
public class WillHide: BaseKeyboardNotification {
override public class var name: Notification.Name {
UIResponder.keyboardWillHideNotification
}
}
public class DidHide: BaseKeyboardNotification {
override public class var name: Notification.Name {
UIResponder.keyboardDidHideNotification
}
}
public class WillChangeFrame: BaseKeyboardNotification {
override public class var name: Notification.Name {
UIResponder.keyboardWillChangeFrameNotification
}
}
public class DidChangeFrame: BaseKeyboardNotification {
override public class var name: Notification.Name {
UIResponder.keyboardDidChangeFrameNotification
}
}
}
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