实施后台位置更新后,FCM 发疯并在没有编程触发的情况下连续发送

Posted

技术标签:

【中文标题】实施后台位置更新后,FCM 发疯并在没有编程触发的情况下连续发送【英文标题】:After implementing background location updates FCM gone mad and are sent continuously without programmed trigger 【发布时间】:2019-05-23 15:22:32 【问题描述】:

我正在使用 Firebase 创建简单的聊天应用。我的数据库有消息节点,当用户从另一个用户获取消息时 - 云功能监控更改并将 Firebase 云消息发送到用户的令牌。 UNUserNotificationCenterDelegate 已在 AppDelegate 中正确实施。 一切正常,直到我开始使用 CoreLocation locationManager.startUpdatingLocation() 方法实现后台位置更新。我也在 AppDelegate 中实例化 locationManager。 我的应用程序将当前位置保存到 UserDefaults(),然后更新位置,将其与 UserDefaults 中保存的位置进行比较,如果它远离保存的位置或在一段时间内未保存到数据库,当前位置将保存到 Firebase 数据库并且是在 UserDefaults() 更新。 运行应用程序后,我开始从 Firebase(前台和后台)收到通知,这些通知是用户收到新消息的最后 5-8 条(然后一次最多 58 条)消息。

我试图了解是什么触发了这些 FCM,但只能将 notification.description 打印到控制台

我试过了 func applicationDidBecomeActive(_ application: UIApplication) // 重新启动任何在应用程序处于非活动状态时暂停(或尚未启动)的任务。如果应用程序之前在后台,可选择刷新用户界面。 UNUserNotificationCenter.current().removeAllPendingNotificationRequests() UNUserNotificationCenter.current().removeAllDeliveredNotifications()

但是没用

import UIKit
import Firebase
import UserNotifications
import GoogleMobileAds
import CoreLocation


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate 

    var window: UIWindow?
   lazy var locationManager: CLLocationManager = 
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.allowsBackgroundLocationUpdates = true
        return locationManager
    ()

    var currentLocation: CLLocation?
    var defaults = UserDefaults.standard
    var geocoder = CLGeocoder()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.makeKeyAndVisible()
        FirebaseApp.configure()

        self.startLocationService()
        locationManager.startUpdatingLocation()

        self.registerForPushNotifications()

        Messaging.messaging().delegate = self

        return true
    

    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) 

        let dataDict:[String: String] = ["token": fcmToken]
        NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
    

    func applicationDidBecomeActive(_ application: UIApplication) 
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests(    )

UNUserNotificationCenter.current().removeAllDeliveredNotifications()
    

    func registerForPushNotifications() 
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.delegate = self
        notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) 
            (granted, error) in
            guard granted else  return 
            self.getNotificationSettings()
        
    

    func getNotificationSettings() 
        UNUserNotificationCenter.current().getNotificationSettings  (settings) in
            guard settings.authorizationStatus == .authorized else  return 
            DispatchQueue.main.async 
                UIApplication.shared.registerForRemoteNotifications()
            
        
    

    func application(_ application: UIApplication,
                 didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
        let tokenParts = deviceToken.map  data -> String in
            return String(format: "%02.2hhx", data)
        
        print("Registered with device token \(tokenParts.joined())")

    

    func application(_ application: UIApplication,
                 didFailToRegisterForRemoteNotificationsWithError error: Error) 
        print("Failed to register: \(error.localizedDescription)")
    

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 

        print("notification is \(notification.description)")

       completionHandler([.sound])

    

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) 
        print("Received notification \(response.notification)")

    

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) 

        if application.applicationState == .active 
            application.applicationIconBadgeNumber = 0
         else 
            let numberOfUnreadMessages = userInfo["count"] as! String
            if let badgeNumber = Int(numberOfUnreadMessages) 
                application.applicationIconBadgeNumber = badgeNumber
            

            completionHandler(UIBackgroundFetchResult.newData)
        
    


extension AppDelegate: CLLocationManagerDelegate 

    func startLocationService() 
        print("Starting location services in AppDelegate")
        if CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse 

            activateLocationServices()

         else 
            locationManager.requestAlwaysAuthorization()
        
    

    private func activateLocationServices() 
        if UIApplication.shared.applicationState == .active 
            locationManager.startUpdatingLocation()
         else 
            locationManager.startMonitoringSignificantLocationChanges()
        
    

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) 
        if status == .authorizedAlways || status == .authorizedWhenInUse 
            activateLocationServices()
         else 
            print("CLAuthorizationStatus is notDetermined")
        
    

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) 
        print("error happened while getting current location: \(error.localizedDescription)")
    

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) 
        currentLocation = locations.first
        print("AppDelegate got location")

        if defaults.value(forKey: "currentLatitude") == nil 
            let currentLatitude = currentLocation?.coordinate.latitude
            let currentLongtitude = currentLocation?.coordinate.longitude
            let currentAltitude = currentLocation?.altitude
            let timeStamp: Double = Double(Int(NSDate().timeIntervalSince1970))

            defaults.set(currentLatitude, forKey: "currentLatitude")
            defaults.set(currentLongtitude, forKey: "currentLongtitude")
            defaults.set(currentAltitude, forKey: "currentAltitude")
            defaults.set(timeStamp, forKey: "locationUpdated")
            print("UserDefaults updated with current location")
        

        saveLocationToDatabase()

    

    func saveLocationToDatabase() 

        var previousLocation: CLLocation?
        var previuosTime: Double?

        guard let currentLocation = currentLocation, let currentUserUid = defaults.string(forKey: "uid") else  return 

        let previousLatitude = defaults.double(forKey: "currentLatitude")
        let previousLongtitude = defaults.double(forKey: "currentLongtitude")
        previuosTime = defaults.double(forKey: "locationUpdated")

        previousLocation = CLLocation(latitude: previousLatitude, longitude: previousLongtitude)
        let timeStamp: Double = Double(Int(NSDate().timeIntervalSince1970))

        if previousLocation != nil && previuosTime != nil 
            let distance = currentLocation.distance(from: previousLocation!)
            let time =  timeStamp - previuosTime!

            if distance < 100 && time < 60 
                print("distance is \(distance), time is \(time)")
                print("No need to save location")
                return
             else 
                self.saveChangesToDatabase()
            
         else 
            self.saveChangesToDatabase()
        
    

    func saveChangesToDatabase() 
        guard let currentLocation = currentLocation, let currentUserUid = defaults.string(forKey: "uid") else  return 
        let usersRef = Database.database().reference().child("users").child(currentUserUid)
        usersRef.child("location").child("latitude").setValue(currentLocation.coordinate.latitude)
        usersRef.child("location").child("longtitude").setValue(currentLocation.coordinate.longitude)
        usersRef.child("location").child("altitude").setValue(currentLocation.altitude)
        let timeToSet: NSNumber = NSNumber(value: Int(NSDate().timeIntervalSince1970))
        usersRef.child("location").child("updated").setValue(timeToSet)

        print("Location saved to database")

        defaults.set(currentLocation.coordinate.latitude, forKey: "currentLatitude")
        defaults.set(currentLocation.coordinate.longitude, forKey: "currentLongtitude")
        defaults.set(currentLocation.altitude, forKey: "currentAltitude")
        defaults.set(timeToSet, forKey: "locationUpdated")
        print("UserDefaults updated with current location")

        self.geocoder.reverseGeocodeLocation(currentLocation)  (placemarks, error) in
            if let error = error 
                print(error.localizedDescription)
                return
            
            guard let placemark = placemarks?.first else  return 
            if let city = placemark.locality, let state = placemark.administrativeArea 
                let currentPlace = "\(city), \(state)"
                usersRef.child("location").child("currentPlace").setValue(currentPlace)
            
        
    



我的控制台是:

AppDelegate 获得位置 距离为 0.00017503746557926585,时间为 58.0 无需保存位置 AppDelegate 获得位置 位置保存到数据库 UserDefaults 已更新为当前位置 通知是 ,, 触发:>> 通知是 ,, 触发:>>

等等……

【问题讨论】:

【参考方案1】:

问题完全出在 Firebase Cloud Functions 中,与 CoreLocation 服务无关

【讨论】:

以上是关于实施后台位置更新后,FCM 发疯并在没有编程触发的情况下连续发送的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法拦截和禁用默认的后台 FCM 通知并在 firebase 消息传递服务工作者中显示自定义通知

Flutter ios 设备不会从 FCM 通知触发 onMessage。实施 APN 时 Sendtodevice 失败

Android 应用在收到新的 FCM 消息时崩溃(前台和后台)

后台 FCM 触发时生成 Flutter 本地通知

FCM 后台通知在 iOS 中不起作用

Firebase FCM通知click_action有效内容