Swift中基于通知Payload跳转ViewController异常问题排查
问题分析与解决方案
核心问题原因
从你给出的两种推送userInfo对比,结合代码逻辑来看,问题出在推送元数据的格式不一致,而你的代码只兼容了第一种格式:
- 第一种推送:
gcm.notification.meta是JSON字符串(比如"{\"property_id\":\"3\"}"),你的代码通过data(using: .utf8)转成Data再序列化,这部分能正常走通。 - 第二种推送:
gcm.notification.meta是直接的字典类型(或者顶层的meta字段是字典),但你的代码只尝试把它当作字符串处理,导致meta.data(using: .utf8)返回nil,后续解析流程直接中断,NotificationEvent.id没有被赋值,跳转逻辑自然无法触发。
另外,rxEvent函数里还有个潜在问题:每次isFromNotification触发true时,你都会创建一个新的id订阅,但紧接着就调用NotificationEvent.isFromNotification.accept(false),这可能导致延迟1秒后的订阅逻辑还没执行就被打断,或者重复订阅引发混乱。
修复步骤
1. 兼容两种格式的meta解析
修改extractUserInfo函数,同时支持读取gcm.notification.meta和顶层meta,并且处理meta是字符串或直接字典的情况:
func extractUserInfo(userInfo: [String: Any], completion: @escaping () -> Void) { // 优先取gcm.notification.type,没有则 fallback到顶层type let pushType: String? = userInfo["gcm.notification.type"] as? String ?? userInfo["type"] as? String guard let type = pushType, type == "new_property" else { completion() return } Storage.instance.savePush(type) // 优先取gcm.notification.meta,没有则 fallback到顶层meta let metaContent: Any? = userInfo["gcm.notification.meta"] ?? userInfo["meta"] guard let meta = metaContent else { completion() return } var propertyJson: [String: Any]? if let metaString = meta as? String { // 处理第一种情况:meta是JSON字符串 if let data = metaString.data(using: .utf8) { do { propertyJson = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] } catch { print("解析字符串meta出错: \(error)") } } } else if let metaDict = meta as? [String: Any] { // 处理第二种情况:meta是直接的字典 propertyJson = metaDict } guard let json = propertyJson, let propertyIdRaw = json["property_id"], let propertyId = propertyIdRaw as? Int ?? (propertyIdRaw as? String).flatMap(Int.init) else { completion() return } NotificationEvent.isFromNotification.accept(true) NotificationEvent.id.accept(propertyId) completion() }
2. 修复rxEvent的订阅逻辑
把id的订阅移到外部,避免重复订阅,同时用combineLatest确保只有当isFromNotification为true且id有效时才触发跳转:
func rxEvent() { // 合并两个信号,确保状态和ID都满足条件才执行跳转 Observable.combineLatest( NotificationEvent.isFromNotification.asObservable(), NotificationEvent.id.asObservable() ) .filter { $0.0 && $0.1 > 0 } .delay(1, scheduler: MainScheduler.instance) .subscribe(onNext: { [weak self] (isFromPush, propertyId) in guard let self = self, let pushType = Storage.instance.getPush() else { return } if pushType == "new_property" { self.presentPropertyDetailVC(propertyId) } else if pushType == "new_rating" || pushType == "coverage_area" { print(propertyId) } // 重置状态,避免重复触发 NotificationEvent.isFromNotification.accept(false) NotificationEvent.id.accept(0) }) .disposed(by: disposeBag) }
3. 额外建议
- 尽量统一推送服务的
userInfo格式:让两种触发方式的推送返回相同结构的元数据,减少客户端的兼容成本,这是更根本的解决方案。 - 在
presentPropertyDetailVC里加日志,确认navigationController是否不为nil:有时候App冷启动时,导航控制器还没初始化完成,也会导致跳转失败。
内容的提问来源于stack exchange,提问作者King
相关产品推荐
相关产品推荐

