首页 > 解决方案 > 后台位置更新

问题描述

我正在构建一个带有位置服务的应用程序。

我正在使用用户当前位置来获取用户周围的对象。目前运行良好。唯一的问题是,我想在后台使用“signfiicantLocationChanges”为用户创建本地通知,但是当应用程序从函数启动时,AppDelegate对象是.applicationDidFinishLaunching(_:)launchOptionsnil

我想获取后台更新并发出 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 来调试它。也用真机测试过。

我错过了什么?

标签: iosswiftcore-locationcllocationmanagerios13

解决方案


@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 类进行初始化。我希望这将有助于实现您所需的输出。


推荐阅读