You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中基于通知Payload跳转ViewController异常问题排查

问题分析与解决方案

核心问题原因

从你给出的两种推送userInfo对比,结合代码逻辑来看,问题出在推送元数据的格式不一致,而你的代码只兼容了第一种格式:

  1. 第一种推送:gcm.notification.meta是JSON字符串(比如"{\"property_id\":\"3\"}"),你的代码通过data(using: .utf8)转成Data再序列化,这部分能正常走通。
  2. 第二种推送: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:50:02