Swift如何写通知代码?如何避免写出丑陋的通知代码?Swift通知代码

本文为转载文章:具体转载信息请看文章最后

Swift如何写通知代码?如何避免写出丑陋的通知代码?Swift通知代码

大概从入门 iOS 的青铜段位我们就开始使用 NotificationCenter 实现跨层一对多的消息传递,具体实现代码大概如下:

//发送通知
let info = ["newsId": 12345,"comment": Comment.init(user_id: 1, content: "laofeng talk: exp")] as [String : Any]
NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "com.notification.comment"), object: nil, userInfo: info)

//接收通知
notificationHolder1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.init(rawValue: "com.notification.comment"), object: nil, queue: nil) { (notification) in
    guard let userInfo = notification.userInfo as? [String:Any] else { return }
    guard let newsId = userInfo["newsId"] as? Int else { return }
    guard let comment = userInfo["comment"] as? Int else { return }
    print(newsId)
    print(comment)
}

在开发中是否依然倔强的坚持类似上面的写法?如果是的话请继续阅读,其实上面的代码存在一些问题:

  1. 在每个需要收发通知的地方都需要 NSNotification.Name 如果有某个地方通知名写错,则会出现无法接收通知的问题
  2. 在每个需要接收通知的地方都需要解析 userInfo ,如果硬编码key不正确则,解析错误

这样会导致每当我们需要 post 或 observer 通知的时候,都需要重一次写上面的代码。假如需要增加或者删除一个 userInfo 传递的参数,那就需要 CMD + F 找到每一处发送和接收通知的地方进行修改,这样维护起来就非常痛苦,那么是否有更优雅的方式可以组织代码呢?

类型化通知

在 objc.io talk 教程 S01E27-typed-notifications-part中介绍了Typed-Notifications, 使用强类型化通知在收发端只需如下的优雅写法即可拿到已经处理好的数据。

//发送通知
CommentChangeNotification.init(newsId: 12345, comment: Comment.init(user_id: 1, content: "laofeng talk: TypedNotification")).post()
//接收通知
notificationHolder = CommentChangeNotification.registerObserver { (info) in
    print(info.newsId)
    print(info.comment)
}

而通过定义 CommentChangeNotification 实现实现 TypedNotification 协议,通知名、通知数据处理集中在一处,这样在业务中监听通知的地方就不需要每个地方都解析 userInfo 数据,即使后期需要增加删除参数也可在这里集中处理。

// 评论改变通知
struct CommentChangeNotification: TypedNotification {
    //通知名
    static var name: Notification.Name {
        return "com.notification.comment"
    }
    // 通知传递的 userInfo 数据
    let newsId: Int
    let comment: Comment

    var userInfo: [AnyHashable: Any]? {
        return ["newsId": newsId,
                "comment": comment
        ]
    }

    init(_ notification: Notification) {
        newsId = notification.userInfo?["newsId"] as! Int
        comment = notification.userInfo?["comment"] as! Comment
    }

    init( newsId: Int, comment: Comment) {
        self.newsId = newsId
        self.comment = comment
    }
}

TypedNotification 如何实现

1、定义通知描述协议包含 name、userInfo、object。

protocol NotificationDescriptor {
    static var name: Notification.Name { get }
    var userInfo: [AnyHashable: Any]? { get }
    var object: Any? { get }
}
extension NotificationDescriptor {
    var userInfo: [AnyHashable: Any]? {
        return nil
    }

    var object: Any? {
        return nil
    }
}
extension NotificationDescriptor {
    public func post(on center: NotificationCenter = NotificationCenter.default) {
        print(Self.name)
        center.post(name: Self.name, object: object, userInfo: userInfo)
    }
}

2、定义通知数据解析协议,在 observer 的 block 中解析 userInfo。

protocol NotificationDecodable {
    init(_ notification: Notification)
}
extension NotificationDecodable {
    @discardableResult
    public static func observer(on center: NotificationCenter = NotificationCenter.default ,
                                for aName: Notification.Name,
                                using block: @escaping (Self) -> Swift.Void) -> NotificationToken {
        let token = center.addObserver(forName: aName, object: nil, queue: nil, using: {
            block(Self.init($0))
        })
        print(aName)
        return NotificationToken.init(token, center: center)
    }
}

3、 定义类型化协议并实现通知注册方法。

typealias NotificationProtocol = NotificationDescriptor & NotificationDecodable
protocol TypedNotification : NotificationProtocol {
    static func registerObserver(using block: @escaping (Self) -> Swift.Void) -> NotificationToken
}
extension TypedNotification {
    static func registerObserver(using block: @escaping (Self) -> Swift.Void) -> NotificationToken {
        return self.observer(on: NotificationCenter.default, for: Self.name, using: block)
    }
}

4、实现一个新通知只需实现 TypedNotification 即可,NotificationToken 类似于NSKeyValueObservation, 并不需要手动去移除通知,只需管理 NotificationToken 的生命周期就可以了。

总结

类型化通知不管是 ObjC 还是 Swift 都可以实现,本文以 Swift 为例,文中源码可点击 GitHub原文链接 中查看。使用类型化通知可避免分散在各处的 Notification 数据处理,也让通知数据处理更安全且易于维护。其实个人认为通知虽然好用,但不宜滥用,应避免业务代码中通知满天飞的尴尬局面。最后思考个问题比如在 VC 中 有个 ProductView,ProductView中包含 TableView ,TableView 中 Cell 上有一个点击购买事件,这个事件需要将购买信息传递至 Cell、ProductView、VC 那么你会如何分发这个事件呢?

文章原标题:《倔强青铜:如何避免写出丑陋的通知代码》
文章来源: iOSTips
文章作者:老峰
文章原文链接:https://mp.weixin.qq.com/s/DHVqftq1LfQ_IUdpTx8CPg?scene=25#wechat_redirect
感谢作者为普及知识做出的贡献。