LoginSignup
15
9

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-01-08

TL;DR

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

やりたいこと

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

その前に

僕の理解では、iOSでbackground taskを行う場合、workmanager的なものを使って、最小単位15minで処理を行えるという認識だった。
だが、locationに関してのbackground taskは、Significant-change location というものを使えば、アプリをkillしていても時間の制約なしに、500 meter以上の移動を検知してその度に処理をすることができるらしい。

事前準備

  • background_locator という、Significant-change location の機能を含む極めて有難いプラグインを使う
  • このプラグインの基本的な使い方を参考に、基礎となる位置情報の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

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9