实施后台位置更新后,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 失败