.NET MAUI(Single Project)で社内配布用の dogfood ビルドや検証用アプリを作るとき、「Android のパッケージ名 / iOS のバンドルID(ApplicationId)を本番とは別にしたい」という悩みがよく出ます。この記事では、なぜコマンド引数だけで切り替えにくいのか、iOS の Info.plist との関係、実務で破綻しない管理方法を具体例つきで整理します。
.NET MAUI で「ビルド種別ごとに ApplicationId を変えたい」よくある背景
.NET MAUI のアプリを運用していると、同じコードベースでも配布先や用途に応じて“別アプリ”として扱いたい瞬間があります。たとえば次のようなケースです。
- 社内配布(dogfood)用は、本番アプリと同じ端末に共存させたい
- 検証環境(staging)と本番環境(production)を、アプリ単位で明確に分けたい
- dogfood 版はアイコンや表示名を変えて、誤操作(本番でテストしてしまう等)を防ぎたい
- iOS のプロビジョニングや証明書の都合で、本番とは別の識別子が必要になった
このときに軸になるのが ApplicationId(Android のパッケージ名 / iOS のバンドルID)です。
ApplicationId とは何か(Android / iOS での意味の違い)
同じ「アプリID」でも、Android と iOS では“どこに効いて、何が変わるか”が微妙に違います。まずは役割を整理しておくと、切り替え設計で迷いが減ります。
| 項目 | Android | iOS | 実務上の意味 |
|---|---|---|---|
| ApplicationId の呼び名 | パッケージ名(applicationId) | バンドルID(Bundle Identifier) | OS がアプリを一意に識別するための ID |
| どこに反映されるか | AndroidManifest などのビルド生成物 | Info.plist の CFBundleIdentifier に相当 | 配布・インストール・アップデートの前提になる |
| 変えるとどうなるか | 別アプリ扱い(並行インストール可能) | 別アプリ扱い(並行インストール可能) | 既存アプリの上書き更新/データ引き継ぎに影響 |
| 周辺設定の影響 | 署名鍵、Deep Link、Firebase 等 | プロビジョニング、Entitlements、Push 等 | 「ID だけ変えて終わり」にならないことが多い |
.NET MAUI(Single Project)では .csproj に <ApplicationId> を置く形が一般的です。また、iOS は最終的に Info.plist の CFBundleIdentifier に関係するため、iOS 特有の制約(署名やプロビジョニング)もセットで考える必要があります。
結論:コマンドやパラメータで ApplicationId を“任意に差し替える”のは難しい
質問として多いのが、「dotnet build の引数やパラメータで ApplicationId を切り替えられないか?」です。スレッドの結論としては、次の理解が現実的です。
- コマンドやパラメータだけで(外から値を渡して)ApplicationId を自由に切り替えるのは難しい
- 特に iOS は、アプリ識別子が Info.plist(CFBundleIdentifier) や 署名(証明書・プロビジョニング) と強く結びつく
- そのため案内としては、まず Info.plist を開いて手動でバンドルIDを変更するのが手っ取り早い
- また、ApplicationId を設定していれば iOS の CFBundleIdentifier に反映される性質がある(=片方を変えると片方に影響する)
ここで重要なのは、「切り替えたい」という要求自体は正しい一方で、ApplicationId が“アプリの根幹情報”で、ビルド生成物や署名にまで影響するため、単純にビルド引数で差し替える運用は破綻しやすい、という点です。
なぜ「引数で切り替え」が破綻しやすいのか
理由をもう少し噛み砕くと、次のような問題が起きがちです。
| 論点 | 何が起きるか | 現場で困るポイント |
|---|---|---|
| OS の識別子はビルド/パッケージの基礎 | ApplicationId はインストール単位・更新単位そのもの | 「同じアプリの別ビルド」ではなく「別アプリ」になる |
| iOS は署名とプロビジョニングがバンドルIDに紐づく | バンドルIDが違うと、同じ証明書/プロファイルが使えない場合がある | ビルドは通ってもインストールできない/起動できない |
| 外部サービス連携がパッケージ名/バンドルID前提 | Push、Deep Link、Firebase、SSO などが ID で登録されている | ID だけ変えると機能が壊れる(通知が飛ばない等) |
つまり「ビルド時にサッと切り替える」つもりでも、アプリを取り巻く設定一式(署名・サービス設定・URL スキーム等)もセットで切り替えが必要になり、結果的にプロジェクト側で“切り替え前提の構成”を用意しておく方が安全、という結論になりやすいです。
iOS は Info.plist の CFBundleIdentifier が核心になる
iOS 側での識別子は Info.plist の CFBundleIdentifier がキーになります。MAUI のプロジェクト構造では、通常 Platforms/iOS/Info.plist に存在します。
スレッドで案内されている「手動で変更する」手順のイメージは次のとおりです。
Platforms/iOS/Info.plistを開くCFBundleIdentifier相当の値(バンドルID)を dogfood 用に変更する- それに対応した証明書・プロビジョニングプロファイルで署名できるようにする
Info.plist は iOS の「このアプリは何者か」を定義するファイルで、バンドルIDはその中心です。ここを変える以上、署名や配布方法とセットで考える必要があります。
「ApplicationId と CFBundleIdentifier の関係」を意識する
.NET MAUI では .csproj の <ApplicationId> を設定していると、iOS の CFBundleIdentifier に反映される性質があります。ここでハマりやすいのが、「Info.plist を手で変えたのに、ビルドしたら別の値に戻った/上書きされたように見える」パターンです。
対策としては、どこを正として管理するかを先に決めるのが重要です。
- 短期的に 1 回だけ dogfood を作る:Info.plist を手で変えるのが早い
- 継続運用する(dogfood を定期配布する):プロジェクト設定(csproj)側で構成別に管理した方が事故が減る
実務で多い落としどころ:ビルド構成で ApplicationId を“プロジェクト側で”切り替える
「引数で任意の ApplicationId を差し替える」のではなく、あらかじめ Dogfood 用の構成をプロジェクトに用意しておき、その構成でビルドするのが現実的です。記事冒頭の例として提示されているのも、この考え方です。
代表例は .csproj の PropertyGroup を条件付きにする方法です。
<PropertyGroup Condition="'$(Configuration)'=='Dogfood'">
<ApplicationId>com.example.app.dogfood</ApplicationId>
</PropertyGroup>
ここでのポイントは、切り替えの入口が「パラメータで値を渡す」ではなく、構成(Configuration)を選ぶという設計になっていることです。値そのものはプロジェクトファイルで管理されるため、CI/CD でもローカルでも再現性が高くなります。
より安全:ターゲットフレームワークごとに条件を分ける
Single Project は複数ターゲット(Android / iOS / Windows…)を同一 csproj で管理します。ApplicationId を「全ターゲット共通」で変えてよい場合もありますが、状況によっては iOS だけ、Android だけ調整したいこともあります。
その場合は $(TargetFramework) も条件に含めると事故が減ります。
<!-- Android の Dogfood だけ ApplicationId を変える例 -->
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Dogfood|net8.0-android'">
<ApplicationId>com.example.app.dogfood</ApplicationId>
</PropertyGroup>
<!-- iOS の Dogfood だけ ApplicationId を変える例 -->
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Dogfood|net8.0-ios'">
<ApplicationId>com.example.app.dogfood</ApplicationId>
</PropertyGroup>
ターゲットを跨いだ意図しない変更(例:Windows の識別子まで変わってしまう)を防ぎやすくなります。
Dogfood 構成そのものを追加する
構成(Debug/Release など)に Dogfood を追加し、ビルド・配布の導線を明確にします。作業のイメージは次のとおりです。
- Visual Studio でソリューション構成に Dogfood を追加(Release をコピーして作ることが多い)
- Dogfood 構成のときだけ
<ApplicationId>を dogfood 用にする - Dogfood 構成のときだけ、表示名やアイコンも変える(誤操作防止)
「切り替えを人間の記憶に頼る」ほど事故は増えます。構成に落とし込んでおくと、リリース作業が属人化しにくくなります。
ApplicationId を変えると“別アプリ”になる:運用上の注意点
ApplicationId(Android のパッケージ名 / iOS のバンドルID)を変えると、端末上では基本的に “別アプリ”扱いになります。これはメリットでもあり、デメリットでもあります。
| 影響 | 起きること | 対策・考え方 |
|---|---|---|
| 上書き更新 | 本番アプリを dogfood 版で更新できない(逆も同様) | 同じ系列(dogfood→dogfood)では同一 ApplicationId を維持する |
| アプリ内データ | ローカルDB/キャッシュ/設定は引き継がれない | 必要ならサーバー側アカウント連携で移行できる設計にする |
| Push 通知 | 別アプリとして登録が必要(APNs/FCM の設定が分かれる) | 環境ごとに Push 設定を分け、混線しないようにする |
| Deep Link / URL Scheme | スキームや関連付けが衝突/未登録になることがある | dogfood 用にスキームやドメインを分離する |
| ユーザーの混乱 | アイコンと表示名が同じだと誤操作しやすい | dogfood は表示名に「Dogfood」「社内」等を付ける |
dogfood の目的が「社内で本番と同等の体験をする」だとしても、識別子を分けた時点で“別アプリとしてのライフサイクル”になることを前提に設計するのが安全です。
iOS の署名はバンドルIDに紐づく:Dogfood 用 ID を作るなら避けて通れない
iOS は特に、バンドルIDを変えると署名周りの整合性が問題になります。dogfood 用のバンドルIDを用意する場合、次の点がポイントです。
- Apple Developer 側で、そのバンドルID(App ID)に対応する設定が必要になることがある
- プロビジョニングプロファイルは、基本的にバンドルIDと結びつく
- Push、Associated Domains、Sign in with Apple などの機能を使う場合は、Entitlements と App ID 設定の整合が必要
実務で多いパターンは、バンドルIDを次のように枝番で分ける運用です。
| 用途 | バンドルID例 | 意図 |
|---|---|---|
| 本番 | com.example.app | ストア配布・本番API |
| Dogfood | com.example.app.dogfood | 社内配布・staging API |
| 検証(任意) | com.example.app.staging | 検証用途をさらに分離 |
この分け方にすると、端末に本番と dogfood を共存させやすく、運用ルールも明確になります。
Dogfood ビルドで一緒に切り替えると事故が減る設定
ApplicationId だけ切り替える運用は、見た目や挙動が本番と同じになりやすく、ヒューマンエラーを誘発します。dogfood を継続運用するなら、次の項目も「構成で切り替える」ことを強くおすすめします。
| 切り替え対象 | なぜ必要か | 例(方針) |
|---|---|---|
| アプリ表示名 | 誤操作防止(本番と区別) | Dogfood のときだけ末尾に「Dogfood」 |
| アプリアイコン | 端末上で瞬時に識別できる | Dogfood 用に色やバッジを付ける |
| 接続先(API/Feature Flag) | 本番に誤ってテストデータを入れない | staging の Base URL を使う |
| ログ設定 | 調査しやすさが変わる | dogfood は詳細ログを許可 |
実装の一例として、Dogfood 構成ではコンパイル定数を足してコード側で分岐できるようにしておくと便利です。
<PropertyGroup Condition="'$(Configuration)'=='Dogfood'">
<ApplicationId>com.example.app.dogfood</ApplicationId>
<ApplicationTitle>ExampleApp Dogfood</ApplicationTitle>
<DefineConstants>$(DefineConstants);DOGFOOD</DefineConstants>
</PropertyGroup>
そしてコード側で次のように分けます。
#if DOGFOOD
var apiBaseUrl = "https://staging-api.example.com";
#else
var apiBaseUrl = "https://api.example.com";
#endif
この方式は、環境変数や外部注入よりも単純で、配布物(IPA/APK)単位で環境を固定できるため、社内配布で混乱が起きにくいのが利点です。
よくある落とし穴と、先に潰しておくチェック項目
ApplicationId を dogfood 用に変える運用では、ビルドが通っても「配布したら動かない」「特定機能だけ壊れる」という落とし穴が出がちです。事前チェックとして使える観点をまとめます。
| 落とし穴 | 症状 | チェック/対策 |
|---|---|---|
| iOS の署名/プロビジョニング不一致 | インストールできない、起動直後に落ちる | dogfood バンドルID用のプロファイル/署名設定を用意 |
| Push 設定が本番のまま | 通知が飛ばない/本番に通知が混線 | APNs/FCM を dogfood 用に分離、キーや証明書も別管理 |
| Deep Link の衝突 | リンクを踏んでも別アプリが起動する | dogfood 専用のスキーム/ドメインを用意 |
| 外部 SDK の登録漏れ | ログイン・計測・クラッシュ収集が動かない | SDK のコンソールで別アプリとして登録が必要な場合がある |
| 表示名とアイコンが同一 | テスターが本番と誤認 | Dogfood と一目で分かる見た目にする |
「どうしても引数で切り替えたい」場合の考え方
CI で「同じソースから複数 ID の成果物を作りたい」要望は実際にあります。ただし、ここまで見てきたとおり、ApplicationId は周辺設定に強く依存します。
そのため現実的には、次のどちらかに寄せるのが安全です。
- プロジェクト内に構成(Dogfood/Release など)を用意し、CI は構成を選ぶだけにする
- どうしても外部から切り替えるなら、ビルド前にファイルをパッチして“プロジェクトそのもの”を切り替えた状態にしてからビルドする
後者は「引数で差し替える」というより、ビルド前処理で .csproj や Info.plist を編集する運用です。再現性や保守性のコストが上がるため、長期運用では前者(構成方式)が選ばれやすいです。
まとめ:ApplicationId 切り替えは“アプリの同一性”を切り替える行為
.NET MAUI(Single Project)で dogfood などビルド種別ごとに ApplicationId(パッケージ名/バンドルID)を切り替えたい場合、単純に dotnet build の引数で任意の値へ差し替える発想は噛み合いにくく、特に iOS は Info.plist(CFBundleIdentifier)と署名の制約が強く効きます。
最短で解決したいなら Info.plist を手で切り替えるのが早い一方、継続配布するなら .csproj の条件付き PropertyGroup で Dogfood 構成を作り、ApplicationId と周辺設定(表示名・アイコン・接続先)まで含めて管理するのが、事故が少ない現実解です。

コメント