Expoにおけるホーム画面ウィジェットとLive Activities
Product • Development • React Native • 2026年3月4日 • 6分で読める
Jakub Grzywacz
Engineering
新しいexpo-widgetsライブラリを使用すると、ネイティブセットアップなしで、Expo UIコンポーネントを使用してホーム画面ウィジェットとLive Activitiesを構築できます。
ホーム画面ウィジェットとLive Activitiesは、iOSアプリが提供できる最も目に見える機能の一部です。よく作られたウィジェットは、アプリが閉じられていてもユーザーの生活の中にアプリを存在させ続けます。これにより、ユーザーの定着率とエンゲージメントが向上します。これは、混雑したApp Storeにおいてますます重要になっています。
最近まで、React Nativeアプリからウィジェットをリリースするには、多くのネイティブな手続きが必要でした:別のXcodeターゲット、データ共有のためのApp Groups、SwiftUIレイアウトコード、そしてその拡張機能をアプリの他の部分と同期し続ける継続的な負担。
このブログ投稿では、Expo Widgetsのアルファ版を紹介します。これは、Continuous Native Generationがネイティブセットアップを処理し、Expo UIコンポーネントを使用してホーム画面ウィジェットとLive Activitiesを構築するための新しい(そして長く待ち望まれた)ライブラリです。
ウィジェットとLive Activitiesとは?
ウィジェットは、ホーム画面やロック画面に配置される小さな、一目で分かるUIサーフェスです。これらはアプリによってリアルタイムで描画されるのではなく、システムによってスケジュールに従って描画されます。ウィジェットはアプリケーションの特定の部分にディープリンクでき、iOS 17以降では、ボタンやトグルでインタラクティブにもなります。
Live Activitiesは、ロック画面とDynamic Islandに表示される時間制限のある更新です。アプリは、イベントが開始されたとき(配達、試合、乗車など)に開始し、アプリ自体またはAPNsプッシュ通知を介して状況が変化するにつれて更新します。イベントが終了すると、アクティビティは終了されます。
ウィジェットをReactに導入
Evan Baconのexpo-apple-targetsはXcodeセットアップを自動化しましたが、SwiftUIでネイティブコンポーネントを書く必要がありました。一方、expo-widgetsでは、ウィジェットをReactコンポーネントとして定義できます。config pluginがWidget Extensionターゲットを生成し、App Groupを設定し、prebuild中に必要なすべてのファイルを作成します。
これを可能にする重要な要素は@expo/uiです。Widget extensionは描画時にReact Nativeを実行できません。これらは、厳しい時間制限の中で、アプリが全く実行されていない可能性がある状態で、システムによってオンデマンドで描画されます。レイアウトはネイティブな形で表現される必要があります。
@expo/uiは、SwiftUIプリミティブに直接マッピングされるText、VStack、HStack、ImageなどのReactコンポーネントを公開します。システムがウィジェットタイムラインを要求すると、コンポーネントは別のJSランタイムで実行され、@expo/uiレイアウトツリーを生成します。ネイティブ側は、その記述を使用してSwiftUIビューを使ってUIを再構築します。実際の描画にはReact Nativeは関与しません。
ウィジェット
ウィジェットは、いくつかの重要な制約を持つ標準的なReactコンポーネントです:
- 自己完結型である必要があります
widgetディレクティブでマークされている必要があります
- 表示データをpropsとして受け取ります(いくつかのウィジェット固有のフィールドに加えて)
データをpropsとして受け取り、どのサイズを埋めているかを示す「family」などのいくつかの追加フィールドがあるため、各サイズに対して別々のウィジェットを定義することなくレイアウトを適応させることができます。
その後、事前にデータでタイムラインをスケジュールでき、システムが自動的に適切なものを表示します。
Live Activities
Live Activitiesは同じメンタルモデル(Expo UIでレイアウトを記述し、propsをプッシュして更新)に従いますが、レイアウトには表示される場所に対して複数の「スロット」があります:
- ロック画面バナー
- Dynamic Islandコンパクト(leading + trailing)
- Dynamic Islandミニマル
- Dynamic Island展開(leading、center、trailing、bottom)
APNsを介してサーバーから直接プッシュ更新を送信したり、push-to-startトークンを使用してユーザーの操作なしでリモートでアクティビティを開始することもできます。
最初のウィジェットの作成方法
ライブラリをインストールし、最初のウィジェットを設定した後、最小限のウィジェットは次のようになります。これは、コーヒーカウントを追跡し、ユーザーがホーム画面から直接それを増加させることができるものです:
import { Button, Text, VStack } from '@expo/ui/swift-ui';
import { font, foregroundStyle } from '@expo/ui/swift-ui/modifiers';
import { createWidget, WidgetBase } from 'expo-widgets';
type Props = {
count: number
};
const CoffeeCounter = (p: WidgetBase<Props>) => {
'widget';
return (
<VStack spacing={8}>
<Text modifiers={[font({ size: 48 })]}>
☕
</Text>
<Text modifiers={[font({ size: 32, weight: 'bold' })]}>
{p.count}
</Text>
<Button
modifiers={[foregroundStyle('white')]}
label="+"
target="increment"
onPress={() => ({ count: p.count + 1 })}
/>
</VStack>
);
};
export default createWidget('CoffeeCounter', CoffeeCounter);
関数本体の上部にある'widget'ディレクティブがそれをウィジェットとしてマークします。onPressは部分的な状態更新を返します。これは現在の状態とマージされ、再描画されます。アプリの起動は必要ありません。
ウィジェットとLive Activityの使用例
ウィジェットは、ユーザーが一日を通して一目で確認することで利益を得るものに適しています:今後のカレンダーイベント、現在の天気状況、タスクカウント、習慣のストリークなど。
Live Activitiesは、明確な終了がある進行中のイベントがある場合に輝きます:ETAカウントダウン付きの食品配達、ゲームクロック付きのライブスポーツスコア、フライト状況更新、ライドシェアドライバーの位置、またはワークアウトタイマー。
重要なのは、アクティビティを開始する自然な瞬間と終了する自然な瞬間があることです。
覚えておくべき良いルール:ウィジェットは「チェックイン」用、Live Activitiesは「進行中」用です。
今後の予定
追加を計画しているいくつかの機能:
- Timeline readback — アプリが閉じられている間に適用されたタイムライン更新をアプリから取得する方法。これにより、状態をアプリロジックに同期し直すことができます。
- Refresh policy — タイムラインをスケジュールする際に、WidgetKitに新しいものを要求するタイミングを指示するリフレッシュポリシーを指定します。例えば、最後のエントリが表示された後や特定の日付に。
- Images — ExpoUIは現在、画像の表示をサポートしていません。
expo-widgetsは現在アルファ版です。フィードバックを収集し、粗い部分を解決する過程で、一部のAPIが変更される可能性があります。皆さんが何を構築し、どう思うかを聞くのを楽しみにしています。何かが動作しない場合や機能リクエストがある場合は、お知らせください!