ios - 后台位置更新
问题描述
我正在构建一个带有位置服务的应用程序。
我正在使用用户当前位置来获取用户周围的对象。目前运行良好。唯一的问题是,我想在后台使用“signfiicantLocationChanges”为用户创建本地通知,但是当应用程序从函数启动时,AppDelegate
对象是.applicationDidFinishLaunching(_:)
launchOptions
nil
我想获取后台更新并发出 HTTP API 请求,根据响应,我将创建本地通知。
这是我的AppDelegate
课:
import UIKit
import UserNotifications
import CoreLocation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var locationManager: LocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Checking this because if the app is started for location updates,
// no need to setup app for UI
if let _ = launchOptions?[.location] {
locationManager = LocationManager()
locationManager?.delegate = self
locationManager?.getCurrentLocation()
return true
}
attemptToRegisterForNotifications(application: application)
if #available(iOS 13, *) { } else {
app.start()
}
return true
}
// MARK: UISceneSession Lifecycle
@available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
@available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
func applicationDidBecomeActive(_ application: UIApplication) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
}
extension AppDelegate: LocatableOutputProtocol {
func didGetCurrentLocation(latitude: Double, longitude: Double) {
UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .authorized {
let content = UNMutableNotificationContent()
content.title = "\(Date().timeIntervalSince1970)"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "\(Date().timeIntervalSince1970)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { _ in
}
}
})
}
func failedGetCurrentLocation(error: Error) {
print(error)
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
private func attemptToRegisterForNotifications(application: UIApplication) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { granted, error in
if let error = error {
print("failed to get auth", error)
return
}
if granted {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
} else {
print("NO AVAIL FOR NOTIFS")
}
})
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.alert)
}
}
我也有一个自定义LocationManager
类:
import CoreLocation
final class LocationManager: NSObject, Locatable {
weak var delegate: LocatableOutputProtocol?
var locationManager: CLLocationManager
override init() {
locationManager = CLLocationManager()
super.init()
let authStatus = CLLocationManager.authorizationStatus()
if CLLocationManager.locationServicesEnabled() {
if (authStatus == .authorizedAlways || authStatus == .authorizedWhenInUse) {
locationManager.delegate = self
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
} else {
locationManager.requestAlwaysAuthorization()
print("we dont have permission")
}
} else {
}
}
func getCurrentLocation() {
locationManager.startUpdatingLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let coordinates = locations.first?.coordinate {
locationManager.stopUpdatingLocation()
self.delegate?.didGetCurrentLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.delegate?.failedGetCurrentLocation(error: error)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("status changed")
if (status == .authorizedAlways || status == .authorizedWhenInUse) {
print("we got permission")
} else {
print("nope")
}
}
}
我正在尝试通过在 Xcode 上创建新模式Wait for executable to be launched
并在模拟器的调试菜单上使用 Freeway Ride 来调试它。也用真机测试过。
我错过了什么?
解决方案
@onurgenes 您需要NotificationCenter
在您的位置管理器初始化部分中使用,如下所示,
import CoreLocation
final class LocationManager: NSObject, Locatable {
weak var delegate: LocatableOutputProtocol?
var locationManager: CLLocationManager
override init() {
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackgroundActive(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForegroundActive(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
locationManager = CLLocationManager()
super.init()
let authStatus = CLLocationManager.authorizationStatus()
if CLLocationManager.locationServicesEnabled() {
if (authStatus == .authorizedAlways || authStatus == .authorizedWhenInUse) {
locationManager.delegate = self
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
} else {
locationManager.requestAlwaysAuthorization()
print("we dont have permission")
}
} else {
}
}
func getCurrentLocation() {
locationManager.startUpdatingLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let coordinates = locations.first?.coordinate {
locationManager.stopUpdatingLocation()
self.delegate?.didGetCurrentLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.delegate?.failedGetCurrentLocation(error: error)
self.locationManager.stopMonitoringSignificantLocationChanges()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("status changed")
if (status == .authorizedAlways || status == .authorizedWhenInUse) {
print("we got permission")
self.locationManager.startMonitoringSignificantLocationChanges()
} else {
print("nope")
}
}
}
@objc private func applicationDidEnterBackgroundActive (_ notification: Notification) {
self.locationManager.startMonitoringSignificantLocationChanges()
}
@objc private func applicationWillEnterForegroundActive (_ notification: Notification) {
self.locationManager.startUpdatingLocation()
}
您需要在 AppDelegate 类上使用此 LocationManager 类进行初始化。我希望这将有助于实现您所需的输出。
推荐阅读
- c++ - 使用结构向量时输出错误
- excel - 如何将值返回到命名范围而不是使用范围的 Offset()
- cordova - 如何在 Cordova 中更改部署目标
- spring - Spring Cloud Gateway 和 Consul Server
- asp.net - 如何连接配置实体框架以连接到 SQL Server?
- c# - 有没有办法稳定增强图像上的 3D 对象
- python-3.x - Python。为什么列表从int转换为字符串后长度会发生变化?
- excel - 以百分比表示的链接值的 Excel 公式
- mysql - 从没有 [column b = 任意值] 实例的表中选择 [column a]
- java - 字符串中的Android转义字符