iOS APP 在背景取得使用者位置(Get location at background)

簡單整理一下iOS如何在背景持續取得使用者位置,本例在收到新定位後會發送一個local notification給自己

  • 在專案中的設定
  1. Target => Capabilities => Background Modes,Switch to ON
  2. 選取Location updates

  • 在plist中加入描敘(這會在提醒使用者同意使用定位時出現)
  1. Privacy - Location Always and When In Use Usage Description
  2. Privacy - Location Always Usage Description
  3. Privacy - Location When In Use Usage Description

  • 實作

1. 在AppDelegate中加入import

    import CoreLocation
    import UserNotifications

2. AppDelegate實作CLLocationManagerDelegate

    class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

3. 加入location manager相關實作


  • 使用locationManager.startMonitoringSignificantLocationChanges(),而非locationManager.startUpdatingLocation。使用方法可參考官方文件,要特別注意的是這個方法取得新定位的觸發時間點在這份官方文件裡有說明如下,大意大概是移動距離500公尺以上才會觸發,且觸發間隔大於5分鐘(我的解讀及實際測試是在行進中,大約每5分鐘觸發一次)

Note
Apps can expect a notification as soon as the device moves 500 meters or more from its previous notification. It should not expect notifications more frequently than once every five minutes. If the device is able to retrieve data from the network, the location manager is much more likely to deliver notifications in a timely manner.


        func startLocationManager() {
            self.locationManager = CLLocationManager()
            self.locationManager.allowsBackgroundLocationUpdates = true // 開啟背景更新(預設為 false)
            self.locationManager.pausesLocationUpdatesAutomatically = false // 不間斷的在背景更新(預設為 true)
            self.locationManager.distanceFilter = kCLDistanceFilterNone
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            self.locationManager.delegate = self
            locationManager.startMonitoringSignificantLocationChanges()
        }
        
        //取得位置成功的callback
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            print("didUpdateLocations: \(locations.last!)")
            
            let location: CLLocation = locations.last!
            
            let latitude: String = String((Float(location.coordinate.latitude)))
            let longitude: String = String((Float(location.coordinate.longitude)))
            
            
            // 1
            let content = UNMutableNotificationContent()
            content.title = "Get New Location 📌"
            content.body = latitude
            content.sound = .default
            
            // 2
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
            let request = UNNotificationRequest(identifier: UUID.init().uuidString, content: content, trigger: trigger)
            
            // 3
            center.add(request, withCompletionHandler: nil)
            
            //do something after get location
            //.....
        }
        
        //取得位置失敗的callback
        func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            print(error.localizedDescription)
        }

4. 在application willFinishLaunchingWithOptions和application didFinishLaunchingWithOptions加入 startLocationManager()和要求使用者收notification

        func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            
            startLocationManager()
            return true
        }
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // Override point for customization after application launch.
         
            //要求接收notification
            center.requestAuthorization(options: [.alert, .sound]) { granted, error in
            }

            startLocationManager()
            return true
        }

5. 在某個View中,要求使用者定位權限

    import UIKit
    import CoreLocation
    class ViewController: UIViewController {
        let locationManager = CLLocationManager()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            //要求App的定位權限
            self.locationManager.requestAlwaysAuthorization()
        }
    }

6. App啟動後,跳出要求使用者授權定位權限,請選"Always Alow"


7. 在Simulator中啟用位置模擬,模擬行進間測試,Debug=>Location=>Freeway Drive













8. 應該就可以在手機上接收到新位置的notification了!



參考資料:
  1.  Handling Location Events in the Background https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/handling_location_events_in_the_background#2865362
  2. Using the Significant-Change Location Service https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_significant-change_location_service
  3. startMonitoringSignificantLocationChanges() https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
  4. Core Location Tutorial for iOS: Tracking Visited Locations https://www.raywenderlich.com/5247-core-location-tutorial-for-ios-tracking-visited-locations
  5. 【iOS - 如何讓 App 在背景不間斷的取得使用者的位置資訊】 https://medium.com/@mikru168/ios-如何讓-app-在背景不間斷的取得使用者的位置資訊-6e17e9c3e281


以上整個專案可以在GitHub上下載

留言

這個網誌中的熱門文章

905 懷孕流水帳

Panasonic NP-TM9-W 洗碗機開箱