はじめに
対象読者
ReactNative、Expoでアプリ開発を行っている人
expo-file-systemを使って画像データをローカルにキャッシュしたい人
npm run iosなどでアプリを立ち上げ直すと画像が表示されず困っている人
読者への思い
Expoでアプリ開発中に画像の保存や表示でかなり詰まったので同じ罠にハマっている人がいたらこれ見て解決してください!
その時の現状と問題点
私はReactNativeとExpo、Firebaseを使ってアプリを開発しており、アプリとFirebase間での通信をなるべく減らすべく、ReactNativeのAsyncStorageとExpoのexpo-file-systemを使って画像をキャッシュをローカルに作れるようにしようとしていました。
- FirebaseのStorageに画像を保存しており、そのファイルパスをFireStoreに保存しています。
- アプリはFireStoreからStorageのファイルパスを取得し、Storageからその画像ファイルをダウンロードします。
- 一度取得した画像はキャッシュとして使い回したいので、expo-file-systemを使用して保存し、expo-file-systemのファイルパスをAsyncStorageで保存していました。
実際に最初にダウンロードし、表示させた結果がこちら
これはリロード(rコマンド)をしても変わらず表示されて期待通りです。
しかし、ctrl+cでアプリを止め、再度npm run ios
でアプリを立ち上げた際に、以下のように画像が表示されなくなってしまいます。
この原因について調べることにしました。
解決法
問題を調べていく中で、画像のファイルパスは正しく保存されており、特にエラーも出ていないことから、expo-file-systemから画像をうまく取得できていないことがわかりました。
そこで調べていくと、、、
こちらの記事に辿り着きました。
結論として、iOSシステムはビルド間でパスが継続することを保証していないため、相対パスを使用することで、異なるアプリビルドやバージョン間でファイルへのアクセスを維持することができるとのことでした。
私はexpo-file-systemの絶対パスをAsyncStorageに保存してそれをImageのsourceに入れていたため、npm run ios
で立ち上げ直すと画像の保存されているパスとは別のパスにアクセスしてしまい、取得できていなかったのです。
使える関数等
ここでは上記の問題を解決するためのコードなどを紹介します。
import * as FileSystem from 'expo-file-system';
export default function getDownloadedImageUri(relativePath) {
return `${FileSystem.documentDirectory}${relativePath}`;
}
こちらは相対パスから画像を取得する関数です。相対パスを引数にFileSystemのパスへ変換します。
import * as FileSystem from 'expo-file-system';
export default async function createImagesDirectory() {
const imagesDir = `${FileSystem.documentDirectory}images`;
const dirInfo = await FileSystem.getInfoAsync(imagesDir);
if (!dirInfo.exists) {
await FileSystem.makeDirectoryAsync(imagesDir, { intermediates: true });
}
}
こちらはFileSystem内にimagesというディレクトリを作成する関数です。こちらを使いimagesの直下に保存するようにすると良いでしょう。
await createImagesDirectory();
const relativePath = `images/${filename}`;
const downloadDest = `${FileSystem.documentDirectory}${relativePath}`;
このようにすることで、画像の保存も取得も可能になります。
まとめ
画像の保存には相対パスが大事!!!