旅行計画アプリ「たびくる」では、旅行の作成、地図、旅程、投票、準備、支払を複数人で扱えるようにしている。開発の中心課題は、画面を増やすことよりも、複数端末で同じ旅行を編集したときに、未同期の変更や支払情報を失わないことだった。
旅行データの正本をCloudKitへ統一する
旅行データは端末のJSONでも保持するが、クラウド上の正本はCloudKitとした。Firebaseアカウント単位のスナップショットを旅行一覧の保存・復元に使う方式は停止し、所有者のprivate databaseと、共有参加者のshared databaseを分けて取得する。
旅行ごとに1つのCustom Record Zoneと全体payloadを持つ構成を維持した。private側とshared側の取得は独立させ、一方が失敗しても、もう一方から取得できた旅行まで破棄しない。共有招待はSwiftUIのscene lifecycleで受け取り、起動前に届いた情報もキューへ保持してからStoreへ渡す。
新規共有の初期値は、リンクを知っている人が参加できる編集可能な共有にした。ただし既存共有の権限は変更しない。所有者は共有管理を行い、editorとviewerの権限はCloudKitの共有設定を正本とする。旅行の同行者名簿と、CloudKit上のアクセス権を同じものとして扱わないことも重要な整理だった。
同期で未保存の編集を上書きしない
共有旅行を再取得したとき、端末で編集中の旅程をクラウド側の古い値で上書きすると、ユーザーの編集が消える。保存中にさらに編集された場合も、先に開始した保存の応答で後から行った編集を消してはいけない。
そこで、取得したchange tagを同期情報へ保存し、別世代への保存は競合として停止するようにした。競合時は未同期編集を自動破棄せず、「共有側の内容を採用」または「この端末の内容を優先」をユーザーが明示的に選ぶ。「この端末の内容を優先」を選んだ場合だけ、CloudKitの全フィールドを保存して共有側を上書きする。
保存応答を反映する前にはローカルの編集時刻を再確認する。同じ旅行の保存を同時実行せず、保存中に増えた編集は次の保存へ回す。通知を取りこぼす可能性に対しては、CloudKit変更通知、前面復帰、手動更新、前面表示中の30秒間隔の補完取得を組み合わせた。
破損データと権限の問題を分けて扱う
今回の調査では、一覧からの編集が再取得で消える経路、読めないローカルJSONを空データで上書きする経路、同名スポットのAI Mapperが重複キーでクラッシュする経路を再現した。
一覧編集ではユーザーが実際に変更したときだけ更新時刻を進めるようにし、初期読込やCloudKit反映を編集扱いにしないようにした。破損したJSONは先に退避し、退避に失敗した場合は元ファイルを上書きしない。同名スポットは自動統合せず、AIの活動を最初の既存スポットへ結び付けて重複キー生成を避けた。
また、viewerが旅行カードを編集・招待・削除できないようにし、共有旅行の削除と共有管理は所有者だけに限定した。同行者名簿から人を削除する場合は、投票や支払の参照先を確認してから関連データを削除する。日数を短縮して予定を削除する場合や、スポット削除で紐付く予定を削除する場合も、確認後にだけ破壊的操作を行う。
M0〜M3の実装と検証
M0/M1では編集消失、空選択による意図しない旅行生成、破損スナップショット、viewer権限、不正な同期メタデータなどを局所的に修正した。M2では旅程・スポット・同行者を削除する際の影響確認を追加し、M3ではFirebase設定ファイルがないcheckoutやPreviewでも未認証状態で起動できるようにし、設定値を含めない開発・配布手順を整理した。
2026年9月23日時点では、Xcode 27.0による署名なしDebug/ReleaseビルドとRelease Archiveに成功し、Tests/Sharing/run.shの共有同期回帰テストも成功している。参加者発見、同名参加者、保存中編集、競合保護、viewer、旧データ互換、破壊的削除の参照整理、JSON再読込などを検証した。
一方、2台の実機によるCloudKit共有、CloudKit Production、Firebase認証の実通信、Foundation ModelsによるAI生成、広告配信、配布Exportは未実施である。Archiveやシミュレータ向けビルドの成功だけで、外部サービスの受入完了とは扱っていない。AIの新規生成では、日別の市区町村指定と旅行全体の目的地指定が衝突する問題も残っており、AI機能は受入対象を限定して保留している。
旅行アプリの共有機能では、同期できることだけでなく、同期に失敗したときに何を保持し、どの操作で上書きするかが重要になる。たびくるでは、CloudKitの権限、端末の未同期編集、破損したローカルデータ、破壊的な編集を別々の境界として扱い、回帰テストで守れる形へ整理している。次は、実機2台とCloudKit環境を使った共有受入で、今回のローカル検証が実際の通知・権限・同期でも成立するかを確認する。