R Roam Roamを入手

iOS のバックグラウンドで GPS を記録し、ライドを失わないために

iOS はいつでもあなたの記録アプリをサスペンドしたり終了させたりできて、しかも予告する義務を負っていません。「どこを通ったか」を書き留めるはずのアプリにとってそれが何を意味するのか、そして実際にライドを守るのは何なのか。

記録を開始して、スマホをポケットに入れ、三時間走り、取り出してみると、アプリは二十分しか記録していない。スマホのトラッカーに対する深刻な苦情としてはこれが群を抜いて多く、そして原因が GPS であることは、まずありません。

iOS があなたのアプリに実際にしていること

バックグラウンドの iOS アプリは、単に遅く動いているわけではありません。状態のあいだを移動します。動作中、サスペンド(メモリ上で凍結され、何も実行しない)、そして終了(消滅)。システムはメモリの逼迫、発熱、バッテリー、そしてシステム自身の判断であなたを状態間へ移しますし、事前に知らせる義務を負っていません。

位置情報のバックグラウンドモードを持つアプリは、位置更新が届いているあいだ動き続けることを許されます。そもそもこの手のアプリが成立しているのは、そのおかげです。通常のアプリと比べれば強い保証です。ただし絶対ではありません。本当にメモリが逼迫したとき——重いゲーム、カメラアプリ、RAM の少ない端末——記録アプリはライドの途中でも落とされます。

ここから、本当に効いてくる失敗が生まれます。アプリがライドをメモリに抱えたまま、停止ボタンが押されるまで何も書き出さない作りだったなら、前回の保存以降はすべて失われます。壊れるのではありません。消えます。エラーも出ません。それを知っていたプロセスが、もう存在しないからです。

唯一の実効的な守り方:届いたそばから書く

ここに気の利いた回避策はありません。守り方は、未保存のデータをそもそも一度も持たないことです。

Roam(ローム)では、位置コールバック一回がバッチ一つであり、データベースのトランザクション一つです。測位点とそのライドの累計値は一緒に確定します。ライドの終わりを待つバッファはなく、書き込み側に点を黙って取りこぼす経路もありません。確定できなかったバッチはエラーとして上がり、握り潰されずにアラームへ積まれます。四十キロ地点でアプリが落とされたなら、四十キロ地点はもう端末の中にあります。

累計値を測位点と同じトランザクションで確定することは、見た目以上に効きます。サマリの行が、ディスク上にない点の集合を記述してしまうことがありえなくなるからです。落とされた後に両者が食い違う瞬間が存在しません。そして復旧のときに、二万八千点を再生して現在地を割り出す必要もありません。行を一つ読むだけで済みます。

同じ考えのもう半分は、何も削除しないことです。フィルタリングも平滑化も自動一時停止も、データベースから行を消しません。それらは測位点に立つフラグとして記録され、生のストリームは届いたままの姿で残ります。経験則が現実のライドを読み違えたときに、元のサンプルがまだそこにあって、導出をやり直せるようにするためです。気に入らなかったサンプルを黙って捨てるアプリは、自分が間違っていた証拠ごと消してしまっています。

終了させられた後、起き上がる

位置情報を「常に許可」にしてあれば、iOS は終了したアプリを、大きな位置の変化をきっかけにバックグラウンドで起動し直すことができます。これは本物の命綱で、そして非常に多くの記録アプリが踏んできた罠を伴います。

その再起動の直後、アプリに与えられているのは非常に短く、しかも予測できない時間です。そして重要なのは、どの中断点でもサスペンドされうるということ。現代の Swift ではそれは、どの `await` でも、という意味になります。再起動してから、位置マネージャを組み直すより先に非同期で何らかの状態を読みにいくアプリは、その途中で凍結されることがあり、再武装を終える前にサスペンドされたプロセスは、二度と GPS から何も聞けません。動いているように見えます。何も受け取っていません。

Roam の再武装が意図的に同期処理になっているのは、まさにこのためです。位置マネージャの組み直しと更新の再開は、最初の await より前に完了します。この間違いを「気をつけましょう」で避けるのではなく、そもそも書き込めない形にしてあります。二度呼ばれても安全に書かれてもいます。バックグラウンドの再起動とフォアグラウンドの起動が競合すると、まさにそれが起きるからです。

はっきりさせておくと、「常に許可」は保険であって必須条件ではありません。「使用中のみ」でも記録はきちんと動きますし、アプリはその旨を許可のダイアログで自分から書いています。強いほうの許可を渡さなければならないかのように匂わせてはいません。

そしてそれは保険であって保証ではない——この区別は言い張る価値があります。Roam の設定自体が、その前提をそのままの言葉で持っています。二度と起動し直されないかもしれない、耐久性だけが約束できることだ、と。iOS のどこにも、終了したアプリを連れ戻す義務はありません。落とされてから起動し直されるまでのあいだに起きたことは、誰にも記録されていませんし、後からそれを渡してくれる API もありません。手元に戻るのは、落とされるまでのライドと、起動し直されてからのライドと、そのあいだの正直な穴です。これを切れ目のない継続として説明するアプリは、プラットフォームが提供していないものを説明しています。

走る側にできること

  1. 長い記録をするなら、位置情報を「常に許可」にしておく。終了が何も奪わずに済むか、その先のライドを全部奪うかの分かれ目になります。
  2. 可能なら低電力モードは切っておく。バックグラウンドの処理が切り詰められやすくなります。
  3. ライドの途中で App スイッチャーからアプリをスワイプして消さない。強制終了は「意図してやった」と iOS に伝えることなので、バックグラウンドでの再起動は起きません。
  4. 記録アプリを二つ同時に走らせない。GPS を起こし続けるアプリが二つあれば、消費電力も、どちらかを殺すことになるメモリの逼迫も倍になります。
  5. iOS のアップデート後に許可を確認する。更新の際に「常に許可」が「使用中のみ」へ、大した断りもなく下がっていることがあります。

自分の記録アプリが安全かどうかを見分ける

特別な道具は要りません。十分あれば試せます。記録を開始し、五分歩き、App スイッチャーからアプリを強制終了して、開き直す。その五分がそこにあるなら、アプリは走りながら書いていました。ライドが空だったり途中で切れていたりするなら、データはメモリに抱えられていたということで、長いライドでどれだけ失いうるかが、これで正確に分かります。

記録アプリは、宣言するものをできるだけ少なくすべきでもあります。バックグラウンドモードを一つ増やすたびに、誤用で終了させられる理由が一つ増え、App Store の審査で聞かれることが一つ増えます。Roam が宣言しているのは位置情報の一つだけで、リポジトリには、その配列が一つでも増えたらビルドを失敗させるゲートが入っています。

よくある質問

iPhone がバックグラウンドで記録を止めてしまったのはなぜですか

ほとんどの場合、メモリの逼迫で iOS がアプリをサスペンドまたは終了させたか、App スイッチャーから強制終了されたかのどちらかです。位置情報のバックグラウンドモードは記録アプリを通常のアプリよりずっと長く生かしますが、絶対の保証ではありません。だからこそ、生き延びること以上に、測位点が届いたときにアプリが何をするかが効いてきます。

ライドを記録するのに「常に許可」は必要ですか

いいえ。画面を消したままの記録なら「使用中のみ」で足ります。位置情報のバックグラウンドモードが、更新が届いているあいだアプリを動かし続けるからです。「常に許可」が足すのは、終了させられた場合に iOS がバックグラウンドでアプリを起動し直せるという可能性で、つまり長いライドのための保険であって、必須条件ではありません。

バックグラウンドの GPS はバッテリーを食いますか

実際に電力を使います。受信機は動き続け、アプリは測位のたびに起きます。最近のスマホなら数時間のライドは余裕で持ちます。問題になるのは一日がかりのイベントで、そこでは、走りながら書くアプリかどうかが、最後の数分を失うのか一日分を失うのかの差になります。

自分の記録アプリがデータを失うかどうか、どう試せますか

記録を開始して、五分歩き、App スイッチャーからアプリを強制終了して、開き直してください。その五分が残っていれば、そのアプリは走りながら書いています。残っていなければ、ライドをメモリに抱えていたということです。

ライドを失わない記録アプリ

Roam は測位点が届いたその瞬間に、走行中の累計値と同じトランザクションで端末へ書き込みます。信号が切れた区間は直線でつながず、切れた区間として残します。獲得標高は GPS の高度ではなく気圧センサーから読みます。アカウントもサインインも同期もありません。一本まるごと記録して、見て、再生するところまでは無料です。

App Storeでダウンロード

関連記事

記事一覧