メインコンテンツへスキップ

iOSでバックグラウンド位置情報の更新が止まる理由

なぜ Capacitor アプリで iOS がバックグラウンド位置情報の更新を停止するのか:サスペンド、バックグラウンドモード、権限、自動一時停止、精度、各原因の対処法。

iPhoneアプリがバックグラウンドタスクを実行しながら位置情報を追跡している様子

iOSはCore Locationの失敗ではなく、アプリをサスペンドするためバックグラウンド位置情報更新を停止します。アプリが位置情報バックグラウンドモードを宣言し、その CLLocationManagerでバックグラウンド更新を有効にし、まだフォアグラウンドにいる間に開始しない限り、iOSはユーザーが離れて数秒後にプロセスをフリーズし、位置情報の取得をキューイングまたはドロップします。このガイドでは、Capacitorアプリで確認されるすべての原因とそれぞれの修正方法、および @capgo/background-geolocation それらを処理します。

iOSによるバックグラウンドアプリの扱い

ユーザーがホーム画面に戻ったり、端末をロックしたりすると、iOSはアプリに短い猶予を与えた後、アプリをサスペンド(中断)します。サスペンド状態のアプリにはCPU時間が割り当てられません。Capacitor アプリの場合、ネイティブ側とWebViewの両方が停止するため、JavaScriptのコールバックも実行されなくなります。

Appleは、位置情報をアクティブに使用しているアプリに対して例外を設けています。アプリがバックグラウンドでの位置情報利用に設定されており、フォアグラウンドで位置情報セッションが開始されている場合、iOSはアプリの実行を継続し、アップデートを配信し続けます。以下では、その条件を満たす方法と、システムが意図的にそれを破るケースについて説明します。

原因1:位置情報のバックグラウンドモードが設定されていない

がないと location における UIBackgroundModes、いくら code を使っても、アップデートは流れ続けません。これを次に追加してください: ios/App/App/Info.plist:

<key>UIBackgroundModes</key>
<array>
  <string>location</string>
</array>

Xcodeの Signing & Capabilities > Background Modes > Location updates からも有効化できます。これはplistのキーであり、entitlementではないため、provisioning profileの変更は不要です。

その背後にあるネイティブのプロパティは allowsBackgroundLocationUpdatesです。これを true に設定しても、plistにbackground modeがないとアプリがクラッシュします。Capgo プラグインは、渡すだけで自動設定してくれます。 backgroundMessage まで start()、そのため plist のキーはリリース前に必ず設定しておいてください。

App Review では、バックグラウンドモードの正当性が確認されます。レビューノートにどの機能がそれを使用しているかを説明し、レビュアーがその機能を確認できるようにしてください。

原因2: バックグラウンドから更新が開始された

Apple のルール: アプリがフォアグラウンドにある間に開始された場合、バックグラウンドでの更新は継続されます。サイレントプッシュ、バックグラウンドフェッチ、または BGTaskScheduler タスクからセッションを開始しても、標準的な位置情報更新には機能しません。

ユーザー操作やアプリの再開時にトラッキングを開始します:

import { App } from '@capacitor/app';
import { BackgroundGeolocation } from '@capgo/background-geolocation';

let tracking = false;

export async function startTracking() {
  if (tracking) return;
  tracking = true;
  await BackgroundGeolocation.start(
    {
      backgroundMessage: 'Recording your route',
      backgroundTitle: 'Route recording',
      requestPermissions: true,
      distanceFilter: 10,
      url: 'https://api.example.com/v1/points',
    },
    (location, error) => {
      if (error) return console.error(error);
      if (location) console.log(location.latitude, location.longitude);
    },
  );
}

// Re-arm when the user comes back, if the session should be active
App.addListener('appStateChange', ({ isActive }) => {
  if (isActive && shouldBeTracking()) startTracking();
});

declare function shouldBeTracking(): boolean;

原因 3: backgroundMessage が渡されていませんでした

これは Capgo プラグイン特有の問題で、最も一般的なバグ報告です。 backgroundMessage が undefined である場合、プラグインは位置情報マネージャーをフォアグラウンド使用のみで設定します: allowsBackgroundLocationUpdates は false のままとなり、アプリがサスペンドされると更新が停止します。両プラットフォームでメッセージを渡してください。Android では、フォアグラウンドサービス通知のテキストとしても使用されます。

原因 4: 権限レベルが不適切、または「今回のみ許可」

多くの開発者は、バックグラウンドでトラッキングするには Always 権限が必要だと考えています。しかし、そうではありません。Apple のドキュメントによると、When In Use 権限を持つアプリは、アクティブな位置情報サービスがあり、バックグラウンド更新が有効な限り、バックグラウンドで実行を継続します。

Always は別の点で重要です: 終了したアプリの再起動です。アプリを再起動できるのは、リージョン監視、重要変更位置情報、および訪問のみであり、それも Always 権限がある場合に限られます。

2つのユーザー選択が、バックグラウンド追跡を静かに壊します:

  • 「一度だけ許可」 これは一時的な「使用中に許可」で、アプリが使用されなくなると失効します。次のセッションでは再び「未確定」の状態から始まります。
  • 「使用中に許可」から「常に許可」へのアップグレードプロンプトが表示されない 「常に許可」をリクエストすると、iOSはアプリが画面に表示されていないタイミングなど、システムが選んだ時点でアップグレードプロンプトを表示することがあります。

セッションを開始する前に、実際の状態を確認してください:

import { BackgroundGeolocation } from '@capgo/background-geolocation';

const status = await BackgroundGeolocation.checkPermissions();

if (status.location !== 'granted') {
  await BackgroundGeolocation.requestPermissions({ permissions: ['location'] });
}

if (status.backgroundLocation === 'when_in_use') {
  // Background tracking still works for standard updates.
  // Explain why Always helps if you also rely on geofences.
}

iOS では、 backgroundLocation を返します when_in_use または granted / always、推測せずに適切な説明を表示できます。

原因 5: Core Location が自動的に更新を一時停止した

CLLocationManager.pausesLocationUpdatesAutomatically デフォルトでは true。iOS がユーザーが移動していないと判断すると、バッテリー節約のために更新を一時停止します。When In Use アプリの場合、一時停止するとアプリを再度起動するまで位置情報へのアクセスが終了し、Core Location は自動では再開しません。

Capgo プラグインは pausesLocationUpdatesAutomatically = false をネイティブの code に設定しているため、こちらは対応済みです。独自のネイティブ code を実装する場合、または別のプラグインを使う場合は、まずこのプロパティを確認してください。

原因 6: デバイスが静止状態で、フィルター値が大きい

成功率 distanceFilter 50メートル未満の場合、デスクに置かれたスマートフォンは更新を生成しません。ログ上では「追跡が停止した」ように見えますが、これは設計通りの動作です。

  • 静止状態でもハートビートが必要な場合は、大きな距離フィルターではなく、 distanceFilter.
  • 使用する minIntervalMs をネイティブPOSTの送信頻度の上限に使用してください。
await BackgroundGeolocation.start(
  {
    backgroundMessage: 'Vehicle tracking active',
    distanceFilter: 0,
    minIntervalMs: 120_000, // at most one native POST every 2 minutes
    url: 'https://api.example.com/v1/vehicle-points',
  },
  () => {},
);

原因 7: 「正確な位置情報」がオフになっている

iOS 14以降、ユーザーはアプリの「正確な位置情報」をオフにできます。更新は引き続き取得できますが、概略(数キロメートル単位)となり、到着頻度も大幅に低下します。データ内でこれを検出してください(大きな accuracy 値)。正確な位置情報が機能にとって重要である理由をユーザーに説明してください。デリバリーやフィットネスアプリをリリースする場合は、悪い体験の後ではなく、オンボーディング時にこれを示してください。

原因 8: ユーザーがアプリを強制終了した

アプリスイッチャーでアプリをスワイプして閉じると、iOSは標準的な位置情報更新を停止します。プラグインでこれを継続させることはできません。Androidから移行するチームにはよくある驚きで、Androidではフォアグラウンドサービスがタスク削除後も実行を継続できます。

オプション:

  • セッション中に強制終了しないようユーザーに案内してください。多くのフリートアプリはオンボーディングでこれを行っています。
  • 重要な場所の周りにAlways権限付きのジオフェンスを追加し、iOSがバックグラウンドでアプリを再起動できるようにします(進入または退出時)。
  • サーバーがセッションのポイント受信を停止した際に、アプリを再オープンするようユーザーにプッシュ通知でリマインドします。

原因 9: ネイティブ側は動作しているが、JavaScript が実行されていない

微妙な点です。ネイティブのcodeは、WebViewがサスペンド中、またはまだ生存しているがタイマーやネットワークコールを素早く処理していない状態でも位置情報を取得できます。もし fetch からコールバックでポイントを送信すると、遅延したり、全く届かなかったりします。

プラグインの url オプションを使用してください。各ポイントは、同じJSON形式でネイティブのcodeからPOSTされます。 Location 型に "source": "native"WebView に依存しません。コールバックは UI 更新のみに使用してください。配信はベストエフォートでディスク上のキューがないため、ポイントの欠落が許されない場合は、コールバック発火時にローカルにも保存し、後で調整してください。

位置情報以外のバックグラウンド処理については、 Capacitor でのバックグラウンドタスクの動作方法 を参照し、 @capgo/capacitor-background-task.

原因 10: 低電力モードとサーマルステート

低電力モードではシステム全体のバックグラウンド活動が減少します。位置情報は動作しますが、セッションが依存する他の処理(ネットワークのリトライ、バックグラウンドリフレッシュ)は遅くなります。リリースを承認する前に、低電力モードをオンにしてテストしてください。

青いインジケーター

「使用中のみ」権限を持つアプリがバックグラウンドで位置情報を使用すると、iOS はステータスバーに青いピルを表示します。Capgo プラグインはバックグラウンドセッション用に showsBackgroundLocationIndicator まで true を設定します。これはユーザーが期待する動作であり、レビュアーも確認します。隠そうとせず、ユーザーの信頼を維持してください。

これらのバグを検知するテストプラン

シミュレーターでのテストだけではバックグラウンド位置情報の検証には不十分です。実機の iPhone を使用し、以下を実行してください:

  1. デバッグだけでなく、リリース設定でビルドしてください。Xcode に接続したデバッグビルドは、ライフサイクルの扱いが異なります。
  2. フォアグラウンドでセッションを開始し、画面をロックして、20 分間歩いたり車に乗ったりしてください。
  3. 「一度だけ許可」、正確な位置情報のオフ、低電力モードのオンでも同様に試してください。
  4. アプリ内のログだけでなく、サーバーのログで source: "native" のポイントを確認してください。
  5. セッション中にアプリを強制終了し、次回起動時に何が起きたかをアプリが説明していることを確認してください。

アドホックビルドにデバイスの UDID が必要な場合、 iOS UDID 検索ツール は Safari から取得します。Xcode 26 付きの Mac でビルドできない場合は、 Capgo のビルド がクラウドで署名済みの iOS ビルドを生成できます。

トラブルシューティング・チェックリスト

症状 考えられる原因 解決策
ロック後数秒で停止 いいえ location バックグラウンドモードの有無 backgroundMessage plistキーを追加してパス backgroundMessage
起動時にクラッシュ allowsBackgroundLocationUpdates バックグラウンドモードなし 追加 UIBackgroundModes → location
一度だけ動作、その後停止 ユーザーが「一度だけ許可」を選択 説明して再質問するか、送信先 openSettings()
駐車時に停止 大きい distanceFilter 下げて使用 minIntervalMs
点の間隔が広く精度が低い 正確な位置情報がオフ ユーザーに有効化を依頼
スワイプで閉じると停止 ユーザーがアプリを終了 iOSでは想定内、再起動にはジオフェンスを使用
ネイティブは受信、サーバーは受信しない JavaScript fetch コールバック内 以下の url オプション

修正のリリース

これらの修正の多くは Info.plist またはネイティブ設定に関わるため、App Storeへの新しいビルドが必要です。権限の説明、オンボーディングの文言、リトライロジックなどのJavaScript部分は、 Capgo Live Updates でネイティブビルド公開後に既存ユーザーへ配信できます。

プラグインの選択に迷っている場合、 Transistorsoftと無料のCapgoプラグインの比較 でトレードオフを解説しています。

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。