web-dev-qa-db-ja.com

バックグラウンドで場所を更新する

ユーザーがアプリを使用していなくてもユーザーの位置情報を取得したいのですが、ホームボタンを押してアプリケーションがバックグラウンド状態になった後、位置情報を取得できますが、数秒後に位置情報の更新が停止しました。そして、私がアプリの場所の更新を強制終了しているときは停止しました。これはアプリデリゲートの私のコードです。

 let locationManager = CLLocationManager()
  var LatitudeGPS = String()
  var LongitudeGPS = String()
  var speedGPS = String()
  var Course = String()
  var Altitude = String()
  var bgtimer = Timer()

func applicationDidEnterBackground(_ application: UIApplication) {

    self.doBackgroundTask()
  }

  func beginBackgroundUpdateTask() {

    backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
      self.endBackgroundUpdateTask()
    })

  }

  func endBackgroundUpdateTask() {
    UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
    self.backgroundUpdateTask = UIBackgroundTaskInvalid
  }
func doBackgroundTask() {

  DispatchQueue.global(qos: .background).async {

    self.beginBackgroundUpdateTask()
    self.StartupdateLocation()
    self.bgtimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.bgtimer(timer:)), userInfo: nil, repeats: true)
    RunLoop.current.add(self.bgtimer, forMode: RunLoopMode.defaultRunLoopMode)
    RunLoop.current.run()
    self.endBackgroundUpdateTask()

  }


}


func bgtimer(timer:Timer!){
  print("Fired from Background ************************************")
  updateLocation()
}
  func StartupdateLocation() {
    locationManager.delegate = self
    locationManager.startUpdatingLocation()
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.distanceFilter = kCLDistanceFilterNone
    locationManager.requestAlwaysAuthorization()
    locationManager.allowsBackgroundLocationUpdates = true
    locationManager.pausesLocationUpdatesAutomatically = false
  }

  func updateLocation() {
locationManager.startUpdatingLocation()
    locationManager.stopUpdatingLocation()
    print("Latitude: \(LatitudeGPS)")
    print("Longitude: \(LongitudeGPS)")
    print("Speed: \(speedGPS)")
    print("Heading: \(Course)")
    print("Altitude BG: \(Altitude)")
    DispatchQueue.main.async {
      print(UIApplication.shared.backgroundTimeRemaining)

    }
  }

  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    LatitudeGPS = String(format: "%.10f", manager.location!.coordinate.latitude)
    LongitudeGPS = String(format: "%.10f", manager.location!.coordinate.longitude)
    speedGPS = String(format: "%.3f", manager.location!.speed)
    Altitude = String(format: "%.3f", manager.location!.altitude)
    Course = String(format: "%.3f", manager.location!.course)

  }
}

数秒後にアプリケーションが終了し、場所の更新が停止したと思います。 20分後にアプリケーションが終了した(OSまたはユーザー)場所の更新を停止して、バッテリーの充電を維持したいと思います。場所の更新での私の問題はどこにありますか。

4
ava

変更することがいくつかあります。

ステップ1:

以下に示すように、プロジェクトの機能セクションでロケーション更新バックグラウンドモードが有効になっていることを確認してください

enter image description here

ステップ2:

そして、私がアプリの場所の更新を強制終了しているときは停止しました。

Apple docsからの引用

このサービスを開始し、その後アプリが終了した場合、新しいイベントが到着すると、システムはアプリをバックグラウンドで自動的に再起動します。このような場合、アプリデリゲートのapplication(:willFinishLaunchingWithOptions :)メソッドとapplication(:didFinishLaunchingWithOptions :)メソッドに渡されるオプションディクショナリには、アプリを示すためのキーの場所が含まれますロケーションイベントのために発売されました。再起動時に、ロケーションマネージャオブジェクトを設定し、このメソッドを呼び出してロケーションイベントの受信を継続する必要があります。位置情報サービスを再開すると、現在のイベントがすぐに代理人に配信されます。さらに、ロケーションマネージャオブジェクトのロケーションプロパティには、ロケーションサービスを開始する前でも、最新のロケーションオブジェクトが入力されます。

リンク: https://developer.Apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati

上記のステートメントで注意すべき重要なことは

再起動時に、ロケーションマネージャオブジェクトを設定し、このメソッドを呼び出して、ロケーションイベントの受信を継続する必要があります。

つまり、現在のロケーションマネージャはあまり役に立たないため、新しいロケーションマネージャを作成し、新しいインスタンスを設定して、もう一度startMonitorSignificantLocationChangesを呼び出す必要があります。

そのため、iOSは、startMonitoringSignificantLocationChangesを使用した場合にのみ、終了したアプリに位置情報の更新を送信します。

これらはすべて、アプリが終了し、iOSが位置情報の更新を受信したときにアプリを再起動した場合にのみ適用されます。ただし、アプリが単にバックグラウンドにある場合は、startMonitorSignificantLocationChangesの使用について何もする必要はありません。

一方、startUpdatingLocationは、アプリがバックグラウンド/フォアグラウンドモードの場合にのみ機能します。アプリが一時停止または強制終了されると、iOSは場所の更新を停止します。

このサービスを開始してアプリが一時停止された場合、システムはアプリの実行が再開されるまで(フォアグラウンドまたはバックグラウンドで)イベントの配信を停止します。アプリが終了すると、新しいロケーションイベントの配信は完全に停止します。したがって、アプリがバックグラウンドでロケーションイベントを受信する必要がある場合は、Info.plistファイルにUIBackgroundModesキー(ロケーション値を含む)を含める必要があります。

リンク: https://developer.Apple.com/documentation/corelocation/cllocationmanager/1423750-startupdatinglocation

したがって、コードを変更します

 locationManager.startMonitoringSignificantLocationChange()

さて、それはstartMonitoringSignificantLocationChangesstartupdatinglocationの適切な使用法についてでした。これで、コードの間違いをタイマーで確認できます。

間違い1:

self.bgtimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.bgtimer(timer:)), userInfo: nil, repeats: true)

タイマーを使用して、場所に関するタイムリーな更新を取得します。それはそれがどのように機能するかではありません!!!タイマーを永久に実行することはできません。アプリが一時停止または終了するとすぐにタイマーが停止します。 Location Managerは、場所が変更されたときにアプリに通知するため、それだけに頼る必要があります。タイマーを実行して、場所の更新をタイムリーに確認することはできません。一時停止または終了した状態では実行されません。

間違い2:

  func updateLocation() {
    locationManager.startUpdatingLocation()
    locationManager.stopUpdatingLocation()

後続のステートメントで更新場所を開始および停止するのはなぜですか?それはあまり意味がありません。

間違い3:

func StartupdateLocation() {
    locationManager.delegate = self
    locationManager.startUpdatingLocation()

StartupdateLocationは複数回呼び出され、このメソッドを呼び出すたびに、ロケーションマネージャーの同じインスタンスでstartUpdatingLocationを繰り返し呼び出します。あなたはそれをする必要はありません! startUpdatingLocationまたはstartMonitoringSignificantLocationChangeは1回だけ呼び出すことができます。

14