You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Swift中FCM通知异常:前台可接收,APP终止后无通知

解决Swift中FCM通知在APP终止时无法触发距离判断的问题

看起来你遇到的核心问题是:APP前台时能正常接收FCM通知并执行距离判断逻辑,但APP终止后,这条逻辑完全没机会运行,导致符合条件的通知无法展示。下面我会一步步拆解问题原因,并给出具体的修复方案:

一、问题根源分析

FCM在iOS上的消息分为两种类型,不同类型在APP终止状态下的处理逻辑完全不同:

  1. 通知消息(Notification Message):APP终止时,系统会直接把通知展示在通知栏,根本不会触发你的didReceiveRemoteNotification或任何自定义回调,你的距离计算逻辑自然无法执行。
  2. 数据消息(Data Message):如果配置正确,系统会唤醒APP在后台短暂运行,触发对应的回调,但你当前的代码没有处理APP终止时的启动参数,也没开启必要的后台权限。

你的当前实现是在收到FCM消息后才计算距离、展示本地通知,但APP终止时,这个触发链条断了。

二、具体修复步骤

1. 调整FCM消息Payload(后端配合)

要让APP终止时也能处理距离判断,必须使用content_available标记的数据消息,后端发送的Payload应该类似这样:

{
  "to": "用户的设备Token",
  "data": {
    "page": "1",
    "msg": "[{\"m\": \"天气提醒--您附近空气质量不佳--37.7749--122.4194--50\"}]"
  },
  "content_available": true,
  "priority": "high"
}
  • content_available: true:告诉APNs唤醒APP在后台处理这条消息
  • priority: "high":确保消息被优先推送(弱网环境下更可靠)

如果用Firebase控制台测试,记得在「Advanced options」里勾选「Content available」。

2. 开启iOS后台推送权限

在Xcode中配置后台模式:

  • 打开项目的Signing & Capabilities面板
  • 添加Background Modes权限,勾选Remote notifications
  • 确保Info.plistUIBackgroundModes包含remote-notification(Xcode会自动帮你添加)

3. 处理APP终止时的启动参数

当APP被FCM数据消息唤醒启动时,通知数据会包含在didFinishLaunchingWithOptions的参数里,你需要在这里触发距离计算逻辑:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 
    FirebaseApp.configure() 
    Messaging.messaging().delegate = self // 把代理设置移到这里,不要等到收到消息再设置
    attemptRegisterForNotifications(application: application) 
    
    // 处理APP终止时收到的FCM数据消息
    if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable: Any] {
        parseReceivedNotification(userInfo: remoteNotification)
    }
    
    return true 
}

4. 优化代码可靠性(避免崩溃)

你的代码里有大量强制解包(!),很容易因数据格式错误导致崩溃,建议改成可选绑定:

// 修改parseReceivedNotification里的距离计算部分
guard let dataUserStorevalue = dataUserinfo1["msg"],
      let data = (dataUserStorevalue as AnyObject).data(using: String.Encoding.utf8.rawValue),
      let dataUservalueget = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String : Any]],
      let dataUservalue = dataUservalueget else {
    print("无效的消息数据")
    return
}

for dataUserinfo in dataUservalue {
    guard let str = dataUserinfo["m"] as? String else { continue }
    let items = str.components(separatedBy: "--")
    guard items.count >= 5,
          let heading = items.first,
          let message = items[safe: 1],
          let latStr = items[safe: 2], let latitude = Double(latStr),
          let lonStr = items[safe: 3], let longitude = Double(lonStr),
          let aqivalue = items[safe: 4],
          let savlatStr = UserDefaultUtilities.getLatitudeOrLongitude(key: CommonUtilities.LATITUDE),
          let savlat = Double(savlatStr),
          let savLongStr = UserDefaultUtilities.getLatitudeOrLongitude(key: CommonUtilities.LONGITUDE),
          let savLong = Double(savLongStr) else {
        print("消息格式错误")
        continue
    }
    
    let distance = mesaureDistance(startX: latitude, startY: longitude, endX: savLong, endY: savlat)
    if distance < 10 {
        self.notification = NotificationObject(heading: heading, message: message, aqi: aqivalue)
        var notificationArray = UserDefaultUtilities.NotificatiionsArray
        notificationArray.append(self.notification)
        CommonUtilities.showWeatherNotificationWithUserInfo(notification: notification, userInfo: global_userInfo )
    }
}

这里用到了一个数组安全访问的扩展(可以自己实现):

extension Array {
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

5. 确保通知权限已正确获取

attemptRegisterForNotifications里,要明确请求用户授权所有需要的通知权限:

func attemptRegisterForNotifications(application: UIApplication) {
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        if let error = error {
            print("请求通知权限失败:\(error)")
            return
        }
        if granted {
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        }
    }
}

三、验证流程

  1. 确保APP已获取通知权限和后台推送权限
  2. 后端发送带content_available: true的数据消息
  3. 杀死APP,等待消息推送
  4. 检查是否在符合距离条件时展示了本地通知

内容的提问来源于stack exchange,提问作者saurabh tripathi

火山引擎 最新活动