プレミアム会員になった瞬間にアプリアイコンが特別なデザインに切り替わる、季節限定のアイコンを期間中だけ出し分ける、ダークモード好きな人だけ別の配色を選べるようにします。こうした「アプリ内からアイコンを動的に切り替える」機能を Rork で組み込もうとしたとき、Expo SDK 標準の expo-image や expo-router を眺めていても答えが見つからず、半日詰まりました。
iOS が公式に提供している Alternate App Icons API を使えば実現できるのですが、Rork(Expo ベース)の場合は Info.plist の手書き編集と asset 配置が必須で、ここを誤るとビルドは通っても切り替えが反応しないか、TestFlight 配布時に弾かれるかのどちらかになります。今回は実際に小さな個人開発アプリでこの機能を入れたときの手順と、つまずいた箇所を残しておきます。
Alternate App Icons とテーマカラー対応アイコンは別物
最初に整理しておきたいのが、よく混同される2つの機能の違いです。
- Themed Icons(iOS 18.0〜): ライト・ダーク・着色(tinted)の3バリアントを OS が自動で切り替える機能。
Assets.xcassets/AppIcon.appiconset/Contents.jsonにappearancesを追加するだけで対応します。ユーザーが手動で選ぶことはできません - Alternate App Icons(iOS 10.3〜): ユーザーまたはアプリのコードから明示的に「次はこのアイコン」と指定して切り替える機能。
UIApplication.setAlternateIconName(_:)を呼びます。Themed Icons とは独立した別の API です(Themed App Icons の対応手順 は別途まとめています)
私が当初混乱していたのは、「ダークモード時に専用アイコンを出したい」という要件で、これは Themed Icons だけで足りるケースでした。一方で「サブスク開始時に金色のアイコンに切り替える」のような明示的な切り替えは Alternate App Icons が必要です。要件を一文で言語化すると、自分が必要なのがどちらの API かはっきりします。
Expo(Rork)で Alternate App Icons を有効化する3ステップ
公式 Apple ドキュメントが解説するのは Xcode プロジェクトを直接いじる前提の手順なので、Expo Managed Workflow ベースの Rork ではそのまま使えません。私が辿り着いた最小構成は次の3ステップです。
ステップ1: アイコン画像を用意する
代替アイコンは AppIcon.appiconset とは別に、ファイル名で参照される独立した PNG として配置します。最低限以下のサイズを揃えます。
[name]@2x.png(120×120)[name]@3x.png(180×180)- iPad もサポートする場合は
[name]~ipad.png(76×76)と[name]~ipad@2x.png(152×152)も追加
私は最初、これを AppIcon-Premium.appiconset のようなアセットカタログに入れてしまい、ビルドは通るのに setAlternateIconName が Image not found を返し続けて1時間つぶしました。Alternate Icons は アセットカタログ外のリソースとして配置するのが正解です。
ステップ2: Info.plist に CFBundleAlternateIcons を追記
Rork で生成されたプロジェクトの app.json(または app.config.ts)に、Expo の infoPlist 設定経由で追記します。
{
"expo": {
"ios": {
"infoPlist": {
"CFBundleIcons": {
"CFBundlePrimaryIcon": {
"CFBundleIconFiles": ["AppIcon"],
"UIPrerenderedIcon": false
},
"CFBundleAlternateIcons": {
"Premium": {
"CFBundleIconFiles": ["icon-premium"],
"UIPrerenderedIcon": false
},
"DarkGold": {
"CFBundleIconFiles": ["icon-dark-gold"],
"UIPrerenderedIcon": false
}
}
}
}
}
}
}ここで指定する CFBundleIconFiles の値は、ステップ1で配置した PNG のベース名です。@2x @3x のサフィックスは含めません。
ステップ3: JavaScript からネイティブ関数を呼ぶ
Expo には Alternate App Icons 公式モジュールがまだ入っていないので、コミュニティ製の expo-dynamic-app-icon を使うのが最速です。
// app/components/IconSwitcher.tsx
import { setAppIcon, getAppIcon } from "expo-dynamic-app-icon";
import { Alert } from "react-native";
export async function switchToPremiumIcon() {
const current = getAppIcon();
if (current === "Premium") return; // すでに切替済みなら何もしない
const result = await setAppIcon("Premium");
// result は "Premium" または false(失敗時)
if (result === false) {
Alert.alert("アイコン切替に失敗しました", "iOS 設定を確認してください");
return;
}
// 切替直後にダイアログが OS から自動表示される(抑制不可)
}
// 期待する出力:
// 成功時 → "Premium" を返し、ホーム画面のアイコンが変わる
// 失敗時 → false を返し、Info.plist 設定の不備を疑う詰まりやすい3つの落とし穴
落とし穴1: Expo Go では絶対に動かない
expo-dynamic-app-icon はネイティブモジュールを必要とするので、Expo Go では setAppIcon is not a function エラーになります。EAS Build でカスタム Dev Client を作ってからでないとローカル検証もできません(Dev Client セットアップ手順 を別記事にまとめています)。私は eas build --profile development --platform ios でビルドしてから初めて挙動確認できました。
落とし穴2: 切替後の OS 標準ダイアログは抑制できない
setAlternateIconName を呼ぶと、iOS が「このアプリのアイコンが変更されました」というアラートを自動的に表示します。これは API レベルで抑制できません。プライベート API を使う回避策がネット上にありますが、App Store 審査でリジェクト対象になるので使わない判断が安全です。代わりに、UI 側で「アイコンが切り替わります。ホーム画面でご確認ください」と事前にメッセージを出してユーザーの心構えを作っておくと、突然のダイアログで驚かれずに済みます。
落とし穴3: アイコンキャッシュが残ってビルド後すぐ確認できない
新しい代替アイコンを Info.plist に追加した直後、シミュレータでビルドしても切替が反映されないことがあります。これは iOS の SpringBoard 側のアイコンキャッシュが原因です(アイコン更新が反映されないトラブル対処 も合わせて参照してみてください)。私が試して効いた手順は、シミュレータのアプリを完全削除 → クリーンビルド → 再インストール、の組み合わせでした。実機でも同じく、一度アンインストールしてから検証するのが確実です。
アクセシビリティとレビュー時の注意点
代替アイコンを実装するとき、私が後から気づいて慌てた点が2つあります。
ひとつは、App Store Connect のレビュー時にプライマリアイコンと alternate icons の両方が「このアプリのブランドを正しく表現しているか」「他社製品と紛らわしくないか」を見られることです。例えば「ダークモード版」を装って実は他社のロゴに似た意匠を入れていると、ガイドライン 4.5.1 で弾かれます。
もうひとつは、UIAccessibility のラベルが代替アイコンに引き継がれない仕様です。VoiceOver ユーザーがホーム画面でアプリを探すとき、アイコン名(ファイル名ベース)がそのまま読み上げられるので、icon-premium-v2-final のような開発者しか分からない名前は避け、Premium Classic のようなユーザー視点の命名にしておきます。
全体を振り返って
Rork で Alternate App Icons を実装するとき、最初に確認すべきは「自分の要件は Themed Icons で足りるのか、本当に明示的な切り替えが必要なのか」です。本当に Alternate Icons が要るのであれば、Info.plist 設定・アセット配置・コミュニティモジュール導入の3点を押さえれば動きます。Expo Go では検証できない、iOS の確認ダイアログは消せない、アイコンキャッシュが残るという3つの罠に最初に気づけると、半日詰まる時間を回避できると思います。
まずは EAS で Dev Client をビルドし、ダミーの2色アイコンで切り替え動作を確認するところから着手するのが安全です。本番デザインは挙動が確定してから差し替えるほうが、不要なリビルドを避けられます。
実装の参考になれば嬉しいです。お読みいただきありがとうございました。