次世代 Expo API:MediaLibrary と Contacts
SDK 55 はコアライブラリの次世代バージョンをもたらします。Shared Objects と Shared Refs は、これらのライブラリとネイティブデータのやり取りの方法を根本から変えます。
日本語
コピー

Shared Objects と Shared Refs は Expo ライブラリの未来だと考えています。不要な結合を生むことなく、モジュール間のより深い統合を可能にするからです。shared objects と shared refs について詳しく知りたい方はこちらの記事をご覧ください。
SDK 54 では File System を更新し、新しいオブジェクト指向 API に置き換えました。次は Contacts と MediaLibrary の番です。更新版はそれぞれ expo-contacts/next と expo-media-library/next として提供されています。今後の SDK リリースでも、同じ仕組みを他のライブラリに広げていく予定です。
それでは、contacts と media library の 2 つのパッケージで具体的に何が変わったのかを見ていきましょう。
MediaLibrary@Next
基本コンセプト
新しい API はシンプルな考え方をもとに作られています。Album と Asset はクラスになり、ネイティブデータのプロキシとして機能します。システムのアルバム内のオブジェクトへの参照を保持し、いつでもメタデータを取得できます。
新しい asset を作成するには、ローカル URI を静的関数 Asset.create() に渡します。この呼び出しでファイルがアルバムに追加され、shared object が返されます:
// Create an asset from a local URI
const asset = await Asset.create('file:///path/to/photo.png');
asset を取得した後の管理は、ずっと直感的です:
// Fetch an album and add the asset directly
const album = await Album.get('Holiday 2026');
await album.add(asset);
// Or create a new album and add the asset in one go
const album = await Album.create('Holiday 2026', [asset]);
プロパティへの直接アクセス
旧 API でよく問題になったのは、getAssetInfoAsync() が返すオブジェクトのサイズでした。位置を確認したいだけなのに、すべてのプロパティを含む巨大なプレーンオブジェクトを取得しなければなりません。MediaLibrary@Next は細かい単位の getter を提供し、必要な部分だけを取得できます。画像のサイズを見るためにメタデータオブジェクト全体を取得する必要はもうありません。
await asset.getLocation(); // { longitude: 50, latitude: 20 }
await asset.getShape(); // { width: 100, height: 100 }
// Need everything? You can still do that:
await asset.getInfo();
強力なクエリ
MediaLibrary@Next は新しいメディアの絞り込み方法を導入しました。新設された Query クラスは builder パターンを採用し、より精度の高いクエリを読みやすく組み立てられます。
対応する述語は gt、gte、lte、lt、eq、within、album で、並べ替え用の orderBy、ページネーション用の limit/offset もあります。
const assets = await new Query()
.eq(AssetField.MEDIA_TYPE, MediaType.IMAGE)
.lte(AssetField.HEIGHT, 1080)
.within(AssetField.WIDTH, [920, 960, 1080])
.orderBy(AssetField.CREATION_TIME)
.limit(20)
.offset(10)
.exe();
Contacts@Next
基本コンセプト
新しい API は次の前提に基づいて設計されています:
Contacts@Next の contact オブジェクトはシステムの連絡先への参照を保持し、基になるシステム連絡先の状態を読み書きするための非同期関数を多数備えています。旧 API と違い、関数呼び出しのたびに ID を手動で渡す必要はありません。特定の contact インスタンスで update を呼ぶだけで済み、よくあるミスをかなり減らせます。
静的メソッドで連絡先を作成・取得・選択できるので、すぐに使い始められます:
// Create a new contact
const contact = await Contact.create({
givenName: 'Andrew',
familyName: 'Jones',
phones: [
{
label: 'mobile',
number: '+12123456789',
},
],
});
// Fetch existing contact
const [contact] = await Contact.getAll({ limit: 1 });
// Or pick one using the system UI
const contact = await Contact.presentPicker();
細かい単位の更新
1 つのプロパティを変えるために巨大な JavaScript オブジェクトを扱いたい人はいません。たとえば旧 API で連絡先に新しいメールアドレスを追加するには、オブジェクト全体を書き換えてから送り返す必要がありました:
// THE OLD WAY
const updatedContact = {
...contact,
emails: [...(contact.emails || []), { address: 'contacts-next@expo.dev', label: 'work' }],
};
await Contacts.updateContactAsync(updatedContact);
同じ操作が今では 1 行で済みます:
// THE NEW WAY
await contact.addEmail({ address: 'contacts-next@expo.dev', label: 'work' });
しかも連絡先のどのプロパティでも同じように変更できます。アバターの設定がどれだけ簡単になったか見てください:
const result = await ImagePicker.launchImageLibraryAsync();
await contact.setImage(result.assets[0].uri);
高性能なクエリ
クエリも同じです。特定のフィールドだけを取得したい場合は、細かい単位の getter を使えます:
const givenName = await contact.getGivenName();
複数のプロパティを取得するには getDetails を使います。必要なフィールドを配列に入れて渡すだけです:
await contact.getDetails([ContactField.EMAILS, ContactField.FULL_NAME]);
連絡先リストを構築しているなら、さらに効率的な方法があります:getAllDetails()。内部的にシステム API の呼び出しが 1 回だけで済むため、パフォーマンスが非常に良く、オプション引数で異なる種類のフィルタリングもできます。
// This is a static module function.
await Contact.getAllDetails([ContactField.EMAILS, ContactField.FULL_NAME]);
また、これらのメソッドが返すオブジェクトは、引数で要求したフィールドに応じて型が絞り込まれます。取得していないフィールドにアクセスすると、TypeScript が警告を出します。
const details = await contact.getDetails([ContactField.GIVEN_NAME]);
console.log(details.givenName); // ✅ "John"
console.log(details.phones); // ❌ TypeScript will show a warning
.patch() + .getDetails() = <3
新しいライブラリには patch() メソッドが加わりました。HTTP プロトコルの同名メソッドと同様、部分的な変更に使います。値が undefined のプロパティはスキップするので、本当に変更したいフィールドだけが更新されます。
getDetails() とスムーズに連携するように設計されています。getDetails() は要求したフィールドだけを取得し、patch() は定義済みのフィールドだけを更新します。1、2 個のフィールドだけを変更するフォームにうってつけです:
const details = await contact.getDetails([ContactField.EMAILS, ContactField.GIVEN_NAME]);
details.givenName = 'John';
await contact.patch(details); // Only updates the givenName in the system.
連絡先のプロパティを完全に置き換えたい場合は、update メソッドを使えます。patch と違い、連絡先全体を上書きします:
await contact.update({
givenName: 'New Name',
familyName: 'New Surname',
// Other fields will be cleared
});
新しいドキュメントをチェック
これらのライブラリはこれで next 回目のイテレーションになるので、皆さんからのフィードバックがこれまで以上に重要です。意見、GitHub issue、機能リクエスト、X への投稿など、どんな形でも歓迎します。とてもありがたいです。
新しいドキュメントもぜひご覧ください。API の実際の使い方の例が載っています!