Flutter iOSで、アプリがkillされていても位置情報を取得して、それをFirestoreに保存したい！

### TL;DR
アプリをkillした状態でもcallbackの中でpluginを使いたい場合は、`AppDelegate` ファイルにそのpluginを定義する必要がある！

### やりたいこと
Flutterアプリで、
「ユーザーのlocationが移動するたびに、Firestoreに位置情報データを保存したい！！！！」
というのを、アプリがfore/background の時では簡単にできたが、kill(終了)している状態でも行いたい！

### その前に
僕の理解では、iOSでbackground taskを行う場合、[workmanager](https://pub.dev/packages/workmanager)的なものを使って、最小単位15minで処理を行えるという認識だった。
だが、locationに関してのbackground taskは、[Significant-change location](https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_significant-change_location_service) というものを使えば、アプリをkillしていても時間の制約なしに、500 meter以上の移動を検知してその度に処理をすることができるらしい。

### 事前準備
- [background_locator](https://pub.dev/packages/background_locator) という、Significant-change location の機能を含む極めて有難いプラグインを使う
- このプラグインの[基本的な使い方](https://github.com/rekab-app/background_locator/wiki/How-to-use)を参考に、基礎となる位置情報のinitiationなどのコードをかく

### 具体的な手順
1. **位置情報の変更を検知する時に呼ばれるcallbackを定義**

```
Future<void> callback(LocationDto locationDto) async {
    print('location in dart: ${locationDto.toString()}');

    // Firebase appが空だったら、初期化
    if (Firebase.apps.isEmpty) {
      await Firebase.initializeApp();
    }
    final _firebaseService = FirebaseService();

　　 //SharedPreferencesでローカルデータを取得
    final prefs = await SharedPreferences.getInstance();
    if (locationDto != null) {
      final myUid = await prefs.getString('uid');
      final currentLatitude = locationDto.latitude;
      final currentLongitude = locationDto.longitude;

      //Firestoreにデータを保存 
      await _firebaseService.updateCurrentLocation(myUid, currentLatitude, currentLongitude); 
    }
    final SendPort send =
        IsolateNameServer.lookupPortByName('terminatedLocator_send_port');
    send?.send(locationDto);
  }
}
```

**注意点**
- callbackの中では、同class内で定義してる他の関数を呼び出してはいけない
- callbackがkill時に呼ばれた時、Firebase appは破棄されている可能性があるので、空だったら初期化するようにする



2.**callback内で呼び出すプラグインを`AppDelegate.swift`に定義**

アプリがkill時に、callback内でプラグインを使いたい場合、`AppDelegate`の方に追記する必要があるらしい

```AppDelegate.swift
import Foundation
import Flutter
import UIKit
import cloud_firestore
import background_locator
import Firebase
import shared_preferences

func registerPlugins(registry: FlutterPluginRegistry) -> () {
    if (!registry.hasPlugin("BackgroundLocatorPlugin")) {
        GeneratedPluginRegistrant.register(with: registry)
    }
}

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    BackgroundLocatorPlugin.setPluginRegistrantCallback(registerPlugins) 
    registerOtherPlugins() //callback内で使うpluginはこの関数内で定義
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
    
  func registerOtherPlugins() {
    if !hasPlugin("io.flutter.plugins.firebase.cloudfirestore") {
        FLTFirebaseFirestorePlugin
            .register(with: registrar(forPlugin: "io.flutter.plugins.firebase.cloudfirestore") as! FlutterPluginRegistrar)
    }
    if !hasPlugin("io.flutter.plugins.sharedpreferences") {
        FLTSharedPreferencesPlugin.register(with: registrar(forPlugin: "io.flutter.plugins.sharedpreferences") as! FlutterPluginRegistrar)
    }
  }
...
}
```

参考document:
- https://github.com/rekab-app/background_locator/wiki/Use-other-plugins-in-callback