Users • React Native • Development • 2026-01-06 • 11分読み物
Clinton Forster(ゲスト著者)
KuratourがどのようにExpoとexpo-audioを使い、オフラインファーストのアーキテクチャでGPSトリガーされたオーディオツアーとツアーオペレーター向けのホワイトラベルアプリを実現しているかを紹介します。Kuratourは、オーストラリア発のフルスタック開発者Clinton Forsterによるアプリで、2025年のAdventure Tourism Awardsで2025 Innovation Awardを受賞しました。
イントロダクション
旅は発見の連続であるべきで、手間やローミング料金、通信状況に邪魔されるべきではありません。多くの旅行者は高いローミング費用や不安定な受信、厳格なツアースケジュールに縛られています。Kuratourはこれを変えるため、スマートフォンを個人用の位置認識型ガイドに変え、世界のどこでも動作するよう設計されています(電波の届かない場所でも動作します)。
Kuratourのビジョン
KuratourはExpoで完全に構築された多言語対応のGPS駆動オーディオツアーアプリです。リアルタイムの位置トリガー、オフライン地図、AI生成ルートにより、徒歩やドライブの没入型体験を提供します。現在は自社プラットフォームだけでなく、オーストラリアをはじめとしたツアーオペレーター向けのホワイトラベルアプリ群にも採用されています。
この記事では、Expo SDKをどのように活用して「オフラインファースト」の課題を解決したか、そして新しいexpo-audioライブラリで現代的なオーディオエンジンをどのように構築したかを解説します。
Expoの基盤:大規模なモダンReact Native
Kuratourは新しいアーキテクチャ上のExpo SDKで完全に構築されており、ファーストパーティモジュールとコミュニティパッケージを組み合わせてネイティブ品質の体験を提供しています。
- コアモジュール
expo-location:リアルタイムGPSトラッキングと領域ベースのトリガー
expo-file-system と expo-sqlite:オフラインデータキャッシュとメディアストレージ
expo-audio:スムーズな多言語再生
expo-video:導入用の多言語説明チュートリアル
expo-splash-screen:起動時の見栄えの良い体験
expo-apple-authentication:iOSでのシームレスログイン
expo-application:アプリバージョンの確認(オフラインコンテンツの整合性チェックのため)
@rnmapbox/maps:対話的でオフライン対応の地図表示
オフラインファースト体験の構築
Kuratourにとって「オフラインファースト」は単なる機能ではなく、コア要件です。旅行者は遠隔地で受信を失ったり、ローミング費用を避けるためにデータ通信を切ることが多いため、次の対策を講じています。
- 地図タイル、画像、オーディオは事前にダウンロードしておく(
expo-file-systemを利用)
- メタデータや訪問履歴はローカルに保存する(
expo-sqliteを利用)
- 接続状態を検知してオンライン/オフラインを自動切替する
expo-locationによるリアルタイムGPSトリガーで、ナレーションを正確なタイミングで再生する
ステップ1:SQLiteで構造化データを管理する
expo-sqliteを用いて、ダウンロード済みツアー、ユーザーの進捗、ローカライズ設定を追跡します。マイグレーションに対応するためにスキーマバージョンを管理しています(例:currentVersion = 3)。主な役割は次のとおりです。
- データベースを開く(例:
openDatabaseSync("db"))
- スキーマバージョンをチェックして必要なマイグレーションを適用
- ツアーオブジェクトをJSON文字列として保存するための
setOfflineData(tourID, offlineData)を提供
ステップ2:アセットダウンロードのオーケストレーション
ユーザーが「Download」をタップしたとき、Mapboxタイル、MP3、高解像度画像など複数リソースを同期する必要があります。expo-file-systemを使ってリモートのディレクトリ構造をローカルにミラーリングします。実装のポイントは:
- CDN上のURLをキーから決定する
- ドキュメントディレクトリ配下にサブディレクトリを作成して保存する(ユーザーの許可を都度取得せずに保管可能)
- ローカルに存在しないファイルのみをダウンロードする(
File.downloadFileAsync相当)
- ローカルURIを
<Audio>や<Image> コンポーネントに渡せるように保存する
ステップ3:UIとの統合
TourDownloadコンポーネント内では、プロセスを順次実行します。最初に地図ルートを取得し、次にメイン画像をダウンロードし、最後にツアー内の各ロケーションについて音声と背景トラックを取得します。完了後、ローカルファイルURIをSQLiteに保存して、ネットワーク接続がなくてもアプリが動作するようにします。
ステップ4:カスタムフックでのスマートな切替
ローカル保存が完了したら、どのソースを優先するかをアプリが判断する必要があります。useOfflineDetectorというカスタムフックを作り、useFocusEffect(React Navigation)を利用して、ユーザーがツアーページに遷移するたびにSQLiteを先にチェックするようにしています。主な挙動は次のとおりです。
getDatabaseService()でDBインスタンスを取得
getOfflineData(tourID)の結果が存在すればオフライン利用可能と判断
- スクリーンがフォーカスされるたびにチェックを再実行
iOSアップデート時のデータ整合性の扱い
iOSでオフラインファーストアプリを作る際のよくある落とし穴は、アプリのアップデート時にDocumentsディレクトリ内のファイルパスが変わったり、ファイルが意図せず削除/孤立する挙動です。SQLiteは通常持ちこたえますが、expo-file-systemで保存したファイルが見つからないと、ユーザーは「ダウンロード済み」ツアーを開いたときに音声が欠落していることに気付きます。これを防ぐために、KuratourではContent Validation Bridgeを実装しています。
ステップ1:アップデートの検知
expo-applicationを使い、現在実行中のアプリバージョン(Application.nativeApplicationVersion)と、ローカルストレージに保存したlastCheckedVersionを比較します。差異がある場合はバリデーションをトリガーします。
ステップ2:ファイルシステムの検証
recheckDownloadedContentは、SQLiteでオフラインフラグの付いた全ツアーを巡回し、ファイルシステム上に資産が実際に存在するか確認します。メイン画像や最初のオーディオストップが1つでも欠けていれば、バックグラウンドで再ダウンロードを行います。
expo-file-systemのファイル存在チェックを行う
- 不足があれば自動的に再ダウンロードをキューに入れる
発見を促進するためのexpo-location活用
Kuratourの魔法は「ハンズフリー」モードにあります。旅行者がポケットにスマホを入れたまま、自動的に座標に基づいてストーリーが再生されるようにしたい。これを実現するために、権限管理と高精度バックグラウンド購読を扱うカスタムフックuseLocationを作りました。
位置監視の実装ポイント:
Location.watchPositionAsyncで数秒ごと、あるいは1メートルの移動ごとに更新を受け取る
accuracy: Location.Accuracy.HighやtimeInterval、distanceIntervalを調整してウォーキングツアーの精度を確保
- フォアグラウンドパーミッションを要求し、許可がなければ購読を開始しない
expo-audioでのレイヤードサウンドのオーケストレーション
Kuratourの体験は単にテキストを読むことではなく、雰囲気を作ることにあります。ナレーション、背景アンビエンス、効果音を重ね合わせ、音量やパンをリアルタイムで制御し、シームレスなクロスフェードを行うためにexpo-audioを利用しています。音声はローカルURIを取得して各オーディオトラックを個別に扱い、以下のような制御を行います:
- ナレーショントラックと背景トラックの独立した再生/停止/フェード
- 場所や方角に応じたパンや音量の調整(定位効果)
- 言語や声の切替(多言語対応)をシームレスに行うためのプリロード
まとめ
KuratourはExpo SDKの力を引き出し、厳しいオフライン要件とロケーション駆動の体験を両立させたアプリです。expo-sqliteでのローカルデータ管理、expo-file-systemによる事前ダウンロード、expo-locationの高精度トラッキング、そしてexpo-audioによる層状オーディオ処理を組み合わせることで、世界中のどこでも動作するハンズフリーの多言語オーディオツアーが実現できました。