iOSアプリで位置情報取得からのバックグラウンド処理をしたときの話

これは2014年頃、Swiftが発表される直前の話。
バックグラウンドになってもどうにか処理をつづけられないかという依頼があって、いろいろためしてみたメモ。
知っての通りiOSにはバックグラウンドに厳しい制約があり基本無理だがいくつか方法はあった

- BackgroundFetch(Background App Refresh Tasks)をつかう 
iOSが任意のタイミングでアプリの処理を30秒だけ実行してくれる。
iOS7からだったので却下した

- VoIPアプリになる
VoIPアプリではないので却下

- 位置情報をバックグラウンドで取得
他アプリでも採用されていた方法だったのでこれをやってみた

いまはBackground ProcessingやBackgroundNotificationの選択肢もあるが当時はなかったので除外

## 位置情報取得
`CoreLocation.framework`の追加、Capabilityから`Background Modes`を追加して`Location updates`にチェックをいれておく
当時はObjective-cで書いていたが、今Objective-cを残してもしょうがないのでSwiftで記載する

```swift
    var locationManager: CLLocationManager = {
        var locationManager = CLLocationManager()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.distanceFilter = 1
        return locationManager
    } ()
```
`allowsBackgroundLocationUpdates`をtrueにしておくとバックグラウンドで受信できる

```swift
    func startLocation() {
        locationManager.startMonitoringSignificantLocationChanges() // 大幅に位置が変更した時のみ位置情報を取得する
    }
```
`startMonitoringSignificantLocationChanges`を使うと位置情報が大きく動いたときだけupdateされる

```swift

extension AppDelegate: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let status = UIApplication.shared.applicationState
        if status != .background {
            return
        }
        NSLog("test application update location")
        task.execute()
    }
}
````
位置情報の変更を検知したらバックグラウンドタスクを実行、今回はバックグラウンドで動いていることがわかりやすいようにタスクの実行をバックグラウンドでのみ行う。

```swift
class BackgroundTask {
    private var loopCount = 0
    private var isExecuting = false
    private let PrefKey = "countValue"
    
    func execute() {
        if isExecuting { return }
        DispatchQueue.global().async {
            self.isExecuting = true
            self.loopCount = UserDefaults.standard.integer(forKey: self.PrefKey)
            while(true) {
                NSLog("test application background task %d", self.loopCount)
                sleep(1)
                self.loopCount = self.loopCount + 1
                UserDefaults.standard.setValue(self.loopCount, forKey: self.PrefKey)
            }
        }
    }
}
```
検証用のバックグラウンドタスク、ただ１秒おきにログを表示するだけ

## 結果
検証はシミュレータのLocationからFree Driveをつかって行った

```
2020-12-06 21:28:29.373625+0900 LaunchLocationInBackground[2189:105006] test application enter foreground
2020-12-06 21:28:32.634231+0900 LaunchLocationInBackground[2189:105006] test application enter background
2020-12-06 21:28:38.703424+0900 LaunchLocationInBackground[2189:105006] test application update location
2020-12-06 21:28:38.710385+0900 LaunchLocationInBackground[2189:105264] test application background task 0
2020-12-06 21:28:39.759649+0900 LaunchLocationInBackground[2189:105264] test application background task 1
2020-12-06 21:28:40.821763+0900 LaunchLocationInBackground[2189:105264] test application background task 2
2020-12-06 21:28:41.858983+0900 LaunchLocationInBackground[2189:105264] test application background task 3
2020-12-06 21:28:42.959856+0900 LaunchLocationInBackground[2189:105264] test application background task 4
2020-12-06 21:28:43.967623+0900 LaunchLocationInBackground[2189:105264] test application background task 5
2020-12-06 21:28:45.003097+0900 LaunchLocationInBackground[2189:105264] test application background task 6
2020-12-06 21:28:46.037451+0900 LaunchLocationInBackground[2189:105264] test application background task 7
2020-12-06 21:28:47.138904+0900 LaunchLocationInBackground[2189:105264] test application background task 8
2020-12-06 21:28:48.235930+0900 LaunchLocationInBackground[2189:105264] test application background task 9
2020-12-06 21:28:49.258731+0900 LaunchLocationInBackground[2189:105264] test application background task 10
2020-12-06 21:29:32.255424+0900 LaunchLocationInBackground[2189:105006] test application update location
2020-12-06 21:29:32.255519+0900 LaunchLocationInBackground[2189:105264] test application background task 11
2020-12-06 21:29:33.323138+0900 LaunchLocationInBackground[2189:105264] test application background task 12
2020-12-06 21:29:34.425526+0900 LaunchLocationInBackground[2189:105264] test application background task 13
2020-12-06 21:29:35.434688+0900 LaunchLocationInBackground[2189:105264] test application background task 14
2020-12-06 21:29:36.526842+0900 LaunchLocationInBackground[2189:105264] test application background task 15
2020-12-06 21:29:37.535671+0900 LaunchLocationInBackground[2189:105264] test application background task 16
2020-12-06 21:29:38.622164+0900 LaunchLocationInBackground[2189:105264] test application background task 17
2020-12-06 21:29:39.658490+0900 LaunchLocationInBackground[2189:105264] test application background task 18
2020-12-06 21:29:39.698484+0900 LaunchLocationInBackground[2189:105006] test application update location
2020-12-06 21:29:40.672103+0900 LaunchLocationInBackground[2189:105264] test application background task 19
2020-12-06 21:29:41.681671+0900 LaunchLocationInBackground[2189:105264] test application background task 20
2020-12-06 21:29:42.747317+0900 LaunchLocationInBackground[2189:105264] test application background task 21
2020-12-06 21:29:43.818607+0900 LaunchLocationInBackground[2189:105264] test application background task 22
2020-12-06 21:29:44.830959+0900 LaunchLocationInBackground[2189:105264] test application background task 23
2020-12-06 21:29:45.855771+0900 LaunchLocationInBackground[2189:105264] test application background task 24
2020-12-06 21:29:46.954273+0900 LaunchLocationInBackground[2189:105264] test application background task 25
2020-12-06 21:29:48.055699+0900 LaunchLocationInBackground[2189:105264] test application background task 26
2020-12-06 21:29:49.065966+0900 LaunchLocationInBackground[2189:105264] test application background task 27
2020-12-06 21:29:50.097980+0900 LaunchLocationInBackground[2189:105264] test application background task 28
```

`application enter background`でバックグラウンドに突入してから、`application update location`でアプリが起動し、バックグラウンドタスクが実行されている。
また、バックグラウンド生存期間が終了したあとも位置情報の更新によって続きから実行されている。
昔試したときはバックグラウンドでの実行時間は3分あったきがするのだが短くなったのだろうか