Mac 上で .NET 9/.NET MAUI アプリを iOS シミュレーターにデプロイした途端、NETSDK1032 が出て実行できない——この現象は「ビルドしたバイナリのアーキテクチャ」と「シミュレーター(=ホスト Mac)のアーキテクチャ」が不一致なときに必ず起きます。本記事では仕組みから原因、即効性のある修正、恒久対策、CI・複数構成での運用までを余すことなく解説します。
症状とエラーメッセージ
Mac 単体で iOS シミュレーター起動を試みると、次のようなエラーが表示されます。
error NETSDK1032: The RuntimeIdentifier platform 'iossimulator-x64' and the PlatformTarget 'arm64' must be compatible.
Windows PC からのリモートビルドでは動くのに、同じソースでも Mac では失敗するケースが典型です。これは、プロジェクトかユーザー設定のどこかに「x64 と arm64 が混在する指定」が残っており、.NET SDK が正しく組み合わせられないために発生します。
結論(先に要点)
シミュレーターはホスト Mac と同じ CPU アーキテクチャのバイナリしか動きません。したがって RuntimeIdentifier(RID)と PlatformTarget を必ずそろえます。
| ホスト Mac | iOS シミュレーターで動くバイナリ | 指定すべき RID | 指定すべき PlatformTarget |
|---|---|---|---|
| Intel Mac(x86_64) | x64(x86_64) | iossimulator-x64 | x64 |
| Apple Silicon(arm64) | arm64 | iossimulator-arm64 | arm64 |
これだけで多くのケースは解決しますが、しばしば .csproj では直したのに .csproj.user(ユーザー設定ファイル)に古い値が残っており、ビルド時に後から上書きされて失敗します。次節以降で根本から整えます。
仕組み:なぜ不一致が起きるのか
iOS シミュレーターは iPhone 実機ではなく、Mac の CPU で iOS ランタイムを模擬する仕組みです。ゆえに、シミュレーター用アプリは 実機用とは別アーキテクチャでビルドされます。Apple Silicon Mac なら arm64、Intel Mac なら x64 です。
.NET の観点では次の二つの値が鍵です。
- RuntimeIdentifier(RID):発行・実行先プラットフォームの「OS+CPU」を示す識別子(例:
ios-arm64、iossimulator-arm64、iossimulator-x64)。 - PlatformTarget:生成するアセンブリの CPU ターゲット(
x64/arm64など)。
この二つが矛盾していると .NET SDK は「同じ世界線にいない組み合わせ」と判断し、NETSDK1032 を投げます。たとえば Apple Silicon なのに iossimulator-x64 と arm64 を混在させると、エラーメッセージの通り「x64 と arm64 が非互換」になります。
最短解決レシピ
ホスト CPU を確認する
まず Mac のアーキテクチャを把握します。
uname -m # arm64 または x86_64 が返る
.csproj に明示的な既定値を設定する
Apple Silicon の例:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-ios</TargetFramework>
<RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
<PlatformTarget>arm64</PlatformTarget>
</PropertyGroup>
</Project>
Intel Mac の例:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-ios</TargetFramework>
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
</Project>
.csproj.user を点検・整理する
Visual Studio や Rider はユーザー固有の設定を <プロジェクト名>.csproj.user に保存します。ここに古い PlatformTarget や RuntimeIdentifier が残っていると、ビルド時に .csproj の値が上書きされます。次のいずれかで対処します。
- 安全策:当該プロパティ行(
PlatformTarget/RuntimeIdentifier)を削除。 - リセット:
.csproj.user自体を削除し、IDE に再生成させる。
クリーンビルドする
キャッシュに残った混在バイナリを排除します。
dotnet clean
rm -rf bin obj
dotnet build -f net9.0-ios -c Debug -r iossimulator-arm64 # Apple Silicon 例
# または
dotnet build -f net9.0-ios -c Debug -r iossimulator-x64 # Intel Mac 例
実行して確認する
CLI からシミュレーター起動まで含めて検証するには次で十分です。
dotnet build -t:Run -f net9.0-ios -c Debug -r iossimulator-arm64
Intel Mac は iossimulator-x64 に読み替えてください。
構成を分けて運用する(Debug=シミュレーター/Release=実機)
日常開発はシミュレーター(=ホストと同一アーキテクチャ)、配布は実機(= arm64 固定)という切替を自動化します。次のように Condition を使えば、構成ごとにプロパティを分岐できます。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-ios</TargetFramework>
</PropertyGroup>
iossimulator-arm64
arm64
iossimulator-x64
x64
ios-arm64
arm64
このようにしておけば、手動で切替える手間がなく、NETSDK1032 の再発も抑止できます。
確認方法:ビルド成果物のアーキテクチャを実測する
「正しく設定したはず」を「正しくビルドされた」に落とし込むには、成果物の実体確認が最短です。
# 例: アプリ本体バイナリのパスは環境により異なる
# Apple Silicon / Debug / Simulator(arm64)例
APP=bin/Debug/net9.0-ios/iossimulator-arm64/<YourApp>.app/<YourApp>
lipo -archs "$APP"
# → arm64 が表示されればOK(Intel Macなら x86_64)
lipo -archs で検出されたアーキテクチャが arm64(Apple Silicon)または x86_64(Intel)になっているか必ず確認しましょう。
よくある落とし穴と対処
.csproj を直したのに直らない
.csproj.userの上書きが最有力。まず削除してから再ビルド。- IDE の構成(Debug/Release 切替)や「アーキテクチャ選択」の UI が
PlatformTargetを設定していることがあるので、プロジェクト設定を見直す。 - 複数の
PropertyGroupが競合している場合、後勝ちで上書きされます。迷ったらdotnet build -blでビルドログを採取し、プロパティの最終値を確認します。
Windows からのリモートビルドは動くのに Mac 直だと失敗する
リモートビルド時は Mac 側のエージェントが RID/アーキテクチャを適切に選びます。一方、Mac 直ビルドではローカルの .csproj.user や IDE 設定がそのまま効くため、古い値の混入が顕在化します。ローカルのユーザー設定をクリアしてから、上記の既定値を .csproj に固定しましょう。
マルチターゲットの MAUI で iOS だけ失敗する
<TargetFrameworks>net9.0-ios;net9.0-android;net9.0-maccatalyst</TargetFrameworks> のように複数指定していると、ターゲットごとの条件付き指定が必要です。iOS だけに適用したい値は Condition="'$(TargetFramework)'=='net9.0-ios'" を付けてください。
<PropertyGroup Condition="'$(TargetFramework)'=='net9.0-ios' and '$(Configuration)'=='Debug'">
<RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
<PlatformTarget>arm64</PlatformTarget>
</PropertyGroup>
古いキャッシュで誤検出される
一度でも x64 でビルドした成果物が残っていると、IDE がそれを拾って起動しようとして失敗します。bin/ と obj/ を削除してからビルドしましょう。Git 管理なら git clean -xdf も有効です。
CLI/IDE で .NET のパスが違う
ターミナルと IDE で別の .NET を参照していると、Workload の有無や既定の挙動が変わります。次で確認・統一を。
which dotnet
dotnet --info
dotnet workload list
Apple Silicon で Rosetta を使っている
一部のツールを Rosetta(x64)で起動していると、ツールは x64、実際のビルド対象は arm64 といった捻れが生じます。IDE とターミナルをネイティブ arm64 で起動し、プロジェクトの指定値と矛盾しないようにしましょう。
実機とシミュレーターの違い(混同しないための表)
| 用途 | RID | PlatformTarget | 備考 |
|---|---|---|---|
| iOS 実機 | ios-arm64 | arm64 | 配布・ストア提出はこれ一択。実機の CPU は常に arm64。 |
| iOS シミュレーター(Apple Silicon) | iossimulator-arm64 | arm64 | ホストが Apple Silicon の場合。 |
| iOS シミュレーター(Intel) | iossimulator-x64 | x64 | ホストが Intel の場合。 |
「Release だから ios-arm64 固定」「Debug はホストに合わせて iossimulator-* 」という運用が実務で一番安定します。
恒久対策:チーム・CI で再発を防ぐ
ユーザー設定の排除
- Git で
*.csproj.userをコミットしない(通常は .gitignore に入っています)。 - もし既に入っているなら一度リポジトリから削除し、各自のローカルで再生成させる。
Directory.Build.props で既定を集中管理
ソリューション直下に Directory.Build.props を置くと、すべての子プロジェクトに既定値を配布できます。
<Project>
<PropertyGroup Condition="'$(TargetFramework)'=='net9.0-ios' and '$(Configuration)'=='Debug'">
<RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
<PlatformTarget>arm64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net9.0-ios' and '$(Configuration)'=='Release'">
<RuntimeIdentifier>ios-arm64</RuntimeIdentifier>
<PlatformTarget>arm64</PlatformTarget>
</PropertyGroup>
</Project>
Apple Silicon と Intel を混在運用する場合は、Debug 側を条件分岐(Arm64/X64)させてください。
CI のビルドマトリクス例(GitHub Actions 風)
strategy:
matrix:
config: [Debug, Release]
steps:
- run: dotnet nuget locals all --clear
- run: dotnet restore
- run: dotnet build -f net9.0-ios -c ${{ matrix.config }} -r ${{ matrix.config == 'Release' && 'ios-arm64' || 'iossimulator-arm64' }}
Intel ランナーを使う場合は Debug の RID を iossimulator-x64 に切替えます。
MAUI プロジェクト例:最小構成の雛形
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0-ios;net9.0-android</TargetFrameworks>
<UseMaui>true</UseMaui>
<Nullable>enable</Nullable>
</PropertyGroup>
iossimulator-arm64
arm64
iossimulator-x64
x64
ios-arm64
arm64
この雛形をベースにすれば、開発者のマシン差異に影響されにくいプロジェクトになります。
デバッグのヒント:最終値を「見える化」する
「どこで上書きされているのか?」を調べるには MSBuild のビルドログが確実です。
dotnet build -f net9.0-ios -c Debug -bl
# カレントに msbuild.binlog が生成される(Binary Log Viewer で可視化)
ログ中で RuntimeIdentifier と PlatformTarget の最終決定値を辿れば、.csproj.user や条件分岐の影響が一目瞭然です。
チェックリスト(これだけ見れば直る)
- Mac の CPU を確認:
uname -mがarm64かx86_64か。 .csprojの二項目を統一:RuntimeIdentifierとPlatformTargetをホストに合わせる。.csproj.userを削除:矛盾プロパティが潜んでいないか必ず点検。- クリーンビルド:
dotnet clean→bin/obj削除 → 再ビルド。 - 成果物の実測:
lipo -archsで生成バイナリのアーキテクチャを確認。 - 構成の分離:Debug は
iossimulator-*、Release はios-arm64。
原因別の具体例と修正パターン
原因:IDE のアーキテクチャ選択 UI を誤設定
IDE の「アーキテクチャ」ドロップダウンで x64 を選んだまま Apple Silicon で実行すると、PlatformTarget=x64 が注入されます。arm64 に変更し、.csproj.user の残骸を削除します。
原因:過去の実機配布設定を Debug にも継承
実機向けに ios-arm64/arm64 を設定したあと、それを Debug にも流用するとシミュレーターで起動できません。Release のみに実機設定を残し、Debug はホストに合わせた iossimulator-* に分岐します。
原因:複数の PropertyGroup が衝突
似た条件の PropertyGroup が複数あると、読み込み順の後勝ちで意図せず上書きされます。「ターゲットフレームワーク」「構成」「OS アーキテクチャ」の 3 軸で条件を絞り、唯一のグループで決まるように整理します。
原因:Rosetta を介したビルド
Apple Silicon の IDE を Rosetta で起動していると、内部的には x64 の PlatformTarget が選ばれることがあります。IDE をいったん終了し、アプリケーション情報で「Rosetta を使用して開く」のチェックを外してから再起動してください。
補足:MAUI の典型的な .csproj 読み方
MAUI の iOS プロジェクトでは、おおむね次の項目だけで NETSDK1032 は避けられます。
<TargetFramework>net9.0-ios</TargetFramework>(またはTargetFrameworks)。- Debug 時の
RuntimeIdentifierとPlatformTargetをホストに合わせる。 - Release 時は
ios-arm64/arm64を固定。
他のプロパティ(CodesignKey や Entitlements 等)は署名や配布に関わるもので、本件の不一致エラーとは切り離して考えるのがコツです。
トラブル再現から復旧までの実演(コマンド一式)
Apple Silicon 環境で誤って x64 の RID を指定してしまった場合の再現・復旧フローです。
# 誤設定(わざとエラーを出す)
dotnet build -f net9.0-ios -c Debug -r iossimulator-x64
# → NETSDK1032
# 修正(arm64 にそろえる)
dotnet clean
rm -rf bin obj
dotnet build -f net9.0-ios -c Debug -r iossimulator-arm64
# 実行確認
dotnet build -t:Run -f net9.0-ios -c Debug -r iossimulator-arm64
開発マシンの移行と将来の互換性
Apple Silicon 環境ではシミュレーターの動作・ビルド速度ともに有利です。Intel Mac を併用しているチームは、上記の条件分岐をプロジェクトに組み込んでおくと、開発者のマシン差を意識せずに作業できます。長期的には Apple Silicon への移行を視野に、Debug は iossimulator-arm64 を既定とし、Intel 専用の分岐は当面の互換レイヤーと捉えると運用が楽になります。
FAQ
Q. PlatformTarget を省略したらどうなる?
A. 省略時はツールや環境の既定に任され、IDE/プラットフォームによって変わる余地が残ります。NETSDK1032 を避ける観点では明示指定が安全です。
Q. RuntimeIdentifiers に複数列挙してもよい?
A. 発行(publish)シナリオでは有効ですが、実行対象は 1 つに決まります。開発中の Debug 実行は RuntimeIdentifier を単一に固定するのが確実です。
Q. 実機とシミュレーターでコードを分けたい
A. ビルド時定数(例:DefineConstants)を構成ごとに切り替え、#if で分岐するのが定石です。NETSDK1032 の解消とは独立の話なので、まずはアーキテクチャ不一致を解決してから検討しましょう。
まとめ
NETSDK1032 は「RID と PlatformTarget の不一致」が原因です。iOS シミュレーターはホスト Mac と同一アーキテクチャのみを受け付けるため、Apple Silicon では iossimulator-arm64/arm64、Intel では iossimulator-x64/x64 にそろえます。加えて .csproj.user の上書きを排除し、クリーンビルド&成果物の実測で検証すれば、Mac 単体でも確実に起動できます。Debug=シミュレーター/Release=実機という構成分離を取り入れ、プロジェクト既定として固定しておくことが、再発防止の最短ルートです。

コメント