.NET 9/10 × iOS・iPadOS・macOS 26/Xcode 26:対応状況と安全な移行手順【保存版】

.NET 9/10 と iOS/iPadOS/macOS 26(Xcode 26)対応状況と、安全に移行するための実践ガイド

iOS/iPadOS/macOS 26 と Xcode 26 の登場により、.NET(特に MAUI)による Apple プラットフォーム開発は大きな過渡期を迎えています。本記事では、現場の判断に直結する「いま何が正式にサポートされ、いつ移行すべきか」「.NET 9 のまま何ができて何ができないのか」を、実務の観点で整理。さらに、CI/CD・依存パッケージ・Native 連携まで含めた段階的移行の手引きを、コピペで使えるコマンドと設定例付きでまとめます。

目次

質問概要

  • 疑問点 1:.NET 9(MAUI を含む)で iOS/iPadOS/macOS 26 は正式サポートされるか?
  • 疑問点 2:サポート時期はいつか?
  • 疑問点 3:いま取るべき対策は?

回答・解決策(結論)

<h3>正式サポートは .NET 10 から</h3>
<ul>
  <li>現時点(2025 年 10 月末)で、<strong>.NET 9 に iOS/iPadOS/macOS 26 の公式サポート予定はありません</strong>。</li>
  <li><strong>.NET 10</strong> で各 OS 26 と Xcode 26 をサポート予定。<strong>Release Candidate(RC)は既に Xcode 26 Beta を扱える</strong>構成が提供されており、<strong>一般提供(GA)は 2025 年 11 月頃の見込み</strong>です。</li>
</ul>

<h3>.NET 9 での挙動(できること/できないこと)</h3>
<ul>
  <li><strong>実行自体</strong>:OS 26 を搭載したデバイス上で .NET 9 のアプリが <strong>動作する見込みは高い</strong>(既存 API と ABI 互換が維持される範囲)。</li>
  <li><strong>制限事項</strong>:<strong>新しい SDK(API 26 系)や Xcode 26 でのビルド・署名は非対応</strong>。最新 API を使ったコードの記述・ビルドは不可、ストア審査で「最新 SDK 必須」期日を跨ぐと配布が困難になります。</li>
</ul>

<h3>推奨ワークフロー</h3>
<ol>
  <li><strong>Xcode 25 以前を維持</strong>:Xcode 26 へ上げると .NET 9/MAUI はビルド不可になるため、<strong>開発機の Xcode を 25 系で固定</strong>。</li>
  <li><strong>.NET 10 RC の検証環境を用意</strong>:新機能や動作確認が急務なら、<strong>隔離したマシン/VM で .NET 10 RC + Xcode 26 Beta</strong> を検証。</li>
  <li><strong>正式版リリース後に段階的移行</strong>:.NET 10 GA 後、本番プロジェクトを計画移行。移行前に <strong>NuGet/サードパーティ製ライブラリの OS 26 &amp; .NET 10 対応</strong>を確認。</li>
</ol>

<h3>追加のベストプラクティス</h3>
<ul>
  <li><strong>CI/CD 環境の分離</strong>:新旧 Xcode/SDK の共存は事故のもと。ビルド用マシンを分離し、<code>xcode-select</code> で明示固定。</li>
  <li><strong>公式リポジトリの監視</strong>:<code>dotnet/macios</code> リリースノートや .NET 公式のアナウンスを追う。</li>
  <li><strong>ネイティブ機能へのフォールバック</strong>:OS 26 の新機能を前倒しで使う必要がある場合は、<strong>Swift/Objective‑C でネイティブ実装→.NET からバインド</strong>する暫定策も検討。</li>
</ul>

対応早見表(.NET × Apple OS/Xcode)

観点.NET 9 + Xcode 25.NET 9 + Xcode 26.NET 10 RC + Xcode 26 Beta.NET 10 GA + Xcode 26
OS 26 デバイスでの実行既存 API 範囲で概ね可非推奨/非対応検証用途で可正式サポート
OS 26 の新 API の利用不可不可原則可(RC 仕様)可
Store 提出(最新 SDK 要求期日前)可(ただし期日に注意)不可可(RC 取り扱いに注意)可
CI/CD の安定性高い低い/破綻中(RC の変更に追随必要)高い

ポイントは、「.NET 9 のまま Xcode 26 へ上げない」こと、および 「.NET 10 GA 待ちで段階移行」という 2 点です。

なぜ「Xcode/SDK 固定」が重要なのか(背景の理解)

.NET(特に iOS/macOS 向けの AOT)は、コンパイルから署名・パッケージングまで Apple ツールチェーン(clang、ld64、codesign、notarytool、xcodebuild 等)に依存します。ヘッダや SDK の更新、新しいリンカ・署名仕様の導入は、.NET 側にも 生成コード・バインディング・タスクの追随を要求します。これが、新 OS/Xcode が出るたびに「該当メジャーの .NET」が必要となる根本理由です。

加えて、ストア審査の「最新 SDK 必須」期日(例:新 OS リリース後に一定期間を経て適用)は、「ビルド時点でどの SDK を使ったか」を見ます。動作自体は旧 SDK アプリでも可能な期間があっても、提出やアップデートは拒否される可能性があるため、Xcode のバージョン固定と計画的な更新が極めて重要になります。

すぐにできる防御的アクション(チェックリスト)

  • 開発機:Xcode 25 を維持(自動更新の停止、/Applications/Xcode_25.app へ退避)。
  • CI/CD:ビルドマシンを新旧で分離(例:macos-* ランナーを 2 系列運用)。
  • パッケージ:dotnet list package --outdated --include-transitive で依存の更新計画を作る。
  • 証明書・プロファイル:自動署名/手動署名いずれも更新期限を棚卸し。
  • ストア提出スケジュール:最新 SDK 必須期日を逆算し、機能フリーズ→移行→提出の窓を確保。
<h3>コマンド例(Xcode バージョン固定)</h3>
<pre><code class="language-bash"># 現在の選択中パス確認

xcode-select -p # 旧版 Xcode を /Applications/Xcode_25.app に配置しておく sudo xcode-select -switch /Applications/Xcode_25.app # 必要に応じて環境変数でも固定 export DEVELOPER_DIR=”/Applications/Xcode_25.app/Contents/Developer” # バージョン確認 xcodebuild -version

.NET 10 への段階的移行プラン(推奨)

1. 検証環境の分離

本番用 Mac からは Xcode 26 を遠ざけ、別マシン/VM で「.NET 10 RC + Xcode 26 Beta」を用意。ネットワークや証明書アクセスも最小限に留め、署名/配布の動作確認を段階的に進めます。

<h3>2. プロジェクト設定の最小変更でビルド</h3>
<p>TFM(Target Framework Moniker)は .NET 10 で <code>net10.0-*</code> となる想定です。複数 TFM を併用し、検証中は <strong>スイッチ一つで行き来できる</strong>構成が便利です。</p>
<pre><code class="language-xml">&lt;Project Sdk="Microsoft.NET.Sdk"&gt;

net9.0-ios;net9.0-maccatalyst net10.0-ios;net10.0-maccatalyst enable enable

呼び出し例:

# .NET 10 RC のワークロード更新(検証環境のみ)
dotnet workload update

# net10.0-* に切り替えてビルド(UseNet10=true)

dotnet build -c Release -p:UseNet10=true

# アーカイブを作成(iOS)

dotnet publish -f net10.0-ios -c Release -p:ArchiveOnBuild=true -p:UseNet10=true 
<h3>3. 依存パッケージの互換性確認</h3>
<p>NuGet パッケージ側が OS 26 の新 API や .NET 10 の変更に追随していない場合、<strong>トランジティブ依存</strong>が足を引っ張ることがあります。以下をルーチン化しましょう。</p>
# 直接・間接の更新確認
```

dotnet list package --outdated --include-transitive

# 依存グラフの可視化(サードパーティツールでも可)

dotnet list package --include-transitive </code></pre>

```
<h3>4. ストア提出のスケジューリング</h3>
<p>「最新 SDK 必須」適用日の前に、<strong>機能追加を凍結</strong>して .NET 10 + Xcode 26 の提出を先行。必要に応じて <strong>暫定リリース(先に SDK を更新、機能は温存)</strong>で審査要件だけ満たすのが安全です。</p>
```

  </section>

  <section>
    <h2>OS 26 新機能を最速で使いたい場合の暫定策</h2>
    <p>.NET 10 GA を待たずに OS 26 の API を先取りしたい場合、<strong>ネイティブ実装とバインディング</strong>でフォールバックできます。概念はシンプルです。</p>
    <ol>
      <li>Swift/Objective‑C で <strong>OS 26 の新 API をラップ</strong>した動的フレームワーク(または静的ライブラリ)を用意。</li>
      <li>.NET 側で <strong>Binding プロジェクト</strong>を作成し、<strong>P/Invoke/Obj‑C ランタイム連携</strong>で呼び出す。</li>
      <li>RC/GA で公式バインディングが整ったら、<strong>段階的に置き換え</strong>。</li>
    </ol>
    <p>一例(OS バージョン分岐):</p>
    <pre><code class="language-csharp">if (OperatingSystem.IsIOSVersionAtLeast(26))
{
    // 26 以降でのみ有効な処理
    EnableNewFeatureFor26();
}
else
{
    // 互換コード
    EnableLegacyPath();
}

    <p>この分岐は <strong>実行時判定</strong>で安全にフォールバックできるため、RC 期の変動にも強い実装になります。</p>
  </section>

  <section>
    <h2>やってはいけないこと(事故を防ぐための NG 集)</h2>
    <div style="overflow-x:auto;">
      <table>
        <thead>
          <tr>
            <th>NG 行為</th>
            <th>何が起きるか</th>
            <th>代替案</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>.NET 9 のまま Xcode 26 に更新</td>
            <td>ビルド/署名が失敗、CI が停止</td>
            <td>Xcode 25 を固定、移行用に別マシンで 26 を検証</td>
          </tr>
          <tr>
            <td>検証なしで一斉に .NET 10 RC へ移行</td>
            <td>RC 由来の仕様変更でリグレッション</td>
            <td>プロジェクトを分割し、段階移行とロールバック手段を用意</td>
          </tr>
          <tr>
            <td>新旧 Xcode を同一マシンで頻繁に切替</td>
            <td>キャッシュ/ツールチェーン競合で不安定化</td>
            <td>ビルドマシンを分離、どうしても同居なら <code>DEVELOPER_DIR</code> で明示固定</td>
          </tr>
          <tr>
            <td>ストアの「最新 SDK 必須」日程を無視</td>
            <td>提出拒否、緊急移行で工数倍増</td>
            <td>提出計画を先に確定、SDK 更新だけ先行する暫定版を用意</td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>

  <section>
    <h2>CI/CD 設計の実践ノウハウ</h2>
    <p>CI/CD は <strong>「Xcode のバージョンが唯一の真実」</strong>になるよう設計します。ランナーやエージェント側で明示固定し、パイプラインは <strong>ビルド環境を信じるだけ</strong>に徹するのがコツです。</p>

GitHub Actions の例(Xcode 25 系の固定)

name: iOS Build (.NET 9 + Xcode 25)on: [push, pull_request]

jobs:
build-ios:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
# Xcode 25 系のパスを明示(ホストに合わせて調整)
- name: Select Xcode 25
run: |
sudo xcode-select -switch /Applications/Xcode_25.app
xcodebuild -version
- name: Setup .NET 9
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build MyApp.sln -c Release -f net9.0-ios </code></pre>
GitHub Actions の例(検証用:.NET 10 RC + Xcode 26 Beta)
name: iOS Build (.NET 10 RC + Xcode 26 Beta)
```

on: workflow_dispatch

jobs:
build-ios-rc:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Select Xcode 26 Beta
run: |
sudo xcode-select -switch /Applications/Xcode_26_beta.app
xcodebuild -version
- name: Setup .NET 10 RC
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.100-rc.*'
- name: Update Workloads
run: dotnet workload update
- name: Build (net10.0-ios)
run: dotnet build MyApp.sln -c Release -p:UseNet10=true -f net10.0-ios </code></pre> <p>ポイントは、<strong>パイプラインごとに Xcode を固定</strong>し、新旧を混在させないことです。</p>

  </section>

  <section>
    <h2>Info.plist/Entitlements/署名でハマらないための備え</h2>
    <ul>
      <li><strong>Info.plist:</strong>新 OS で追加されるプライバシーキー/利用目的文字列は、<strong>早めに文面を準備</strong>(後追いで審査差し戻しを防止)。</li>
      <li><strong>Entitlements:</strong>新しい権限が導入された場合に備え、<strong>可否を環境変数で切り替え</strong>られる構成にしておく。</li>
      <li><strong>署名/プロビジョニング:</strong>自動署名は便利だが、RC 期は変動が多いため、<strong>手動署名の手順も残す</strong>(fastlane 等の既存ジョブはそのまま温存)。</li>
      <li><strong>Notarization(macOS):</strong>ツールの仕様差異に敏感。CI では <code>--verbose</code> ログをアーティファクト保存する。</li>
    </ul>
  </section>

  <section>
    <h2>よくあるビルドエラーと回避策</h2>
    <div style="overflow-x:auto;">
      <table>
        <thead>
          <tr>
            <th>症状(例)</th>
            <th>原因の傾向</th>
            <th>回避策</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><code>MTXXXX</code>/<code>MMXXXX</code> 系のリンカエラー</td>
            <td>新旧 SDK のヘッダ不整合、弱参照解決失敗</td>
            <td>Xcode を 25 に固定、<code>bin/obj</code> のクリア、RC では Binding 側の属性見直し</td>
          </tr>
          <tr>
            <td><code>codesign</code> での署名失敗</td>
            <td>ツールチェーンの切替漏れ、キーチェーン参照権限</td>
            <td><code>DEVELOPER_DIR</code> を固定、CI のキーチェーン設定を再発行</td>
          </tr>
          <tr>
            <td>「最新 SDK 必須」関連の提出拒否</td>
            <td>旧 SDK でビルドされた IPA/PKG を提出</td>
            <td>.NET 10 + Xcode 26 で再ビルドし提出、暫定版で要件だけ先行クリア</td>
          </tr>
          <tr>
            <td>ランタイムでのクラッシュ(OS 26 のみ)</td>
            <td>未ガードの API 呼び出し、OS バージョン依存差分</td>
            <td><code>OperatingSystem.Is*VersionAtLeast</code> で分岐、機能フラグで段階配布</td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>

  <section>
    <h2>UI/機能の段階配布とフェイルセーフ設計</h2>
    <p>OS 26 でのみ有効な機能は、<strong>リモートフラグで ON/OFF</strong>できるように実装し、<strong>クラッシュや審査差し戻し時の巻き戻し</strong>を容易にします。アプリ初期化時に OS バージョンとフラグを確認し、利用可否を切り替えます。</p>
    <pre><code class="language-csharp">var enableFeature26 = RemoteConfig.GetBool("feature_os26") 
                      &amp;&amp; OperatingSystem.IsIOSVersionAtLeast(26);
if (enableFeature26)
{
    ShowNewUI();
}
else
{
    ShowExistingUI();
}

  

開発チーム運用のベストプラクティス 分岐戦略:メインブランチは .NET 9 + Xcode 25 の保守、release/next で .NET 10 検証。必要に応じて Git Submodules/Monorepo でパッケージ共通化。 変更凍結のルール化:「SDK 更新前後 2 週間は機能追加を原則禁止」。 回帰テストの自動化:スナップショット UI テストは OS 26 実機を含む矩形サイズで取得。 可観測性:クラッシュログ(symbolicate)とパフォーマンス計測は RC 期に二重化。 技術的補遺:MAUI と各プラットフォーム TFM の考え方 MAUI プロジェクトは、内部的に iOS/iPadOS/macOS(Mac Catalyst 含む)を 個別 TFM としてビルドします。netX.0-ios、netX.0-maccatalyst といった TFM を正しく切り替えられると、検証と本番を安全に同居できます。 <PropertyGroup> <TargetFrameworks>net9.0-ios;net9.0-maccatalyst</TargetFrameworks> <TargetFrameworks Condition="'$(UseNet10)'=='true'">net10.0-ios;net10.0-maccatalyst</TargetFrameworks> <RuntimeIdentifier>ios-arm64</RuntimeIdentifier> <UseMaui>true</UseMaui> </PropertyGroup> ライブラリ側は マルチターゲットにしておくと、OS 26 の機能のみを #if IOS で切替えるなどの設計がしやすくなります。 プロダクトマネージャー向け:意思決定のための比較表 項目 維持案(.NET 9 + Xcode 25) 先行検証案(.NET 10 RC + Xcode 26 Beta) 本番移行案(.NET 10 GA + Xcode 26) 市場投入の速さ 中(既存維持) 高(機能検証を先行) 高(正式) 技術的リスク 低 中(RC の変動) 低 審査通過の確度 期日まで高、以降低 中(RC の扱い次第) 高 開発コスト 低 中(検証環境整備) 中(本番移行工数) 推奨は「現行維持+検証先行+本番で GA」の 3 段ロケット。これが総コストとリスクのバランスに優れます。 FAQ(よくある質問) Q. .NET 9 のまま OS 26 の端末でユーザーは使えますか? A. 既存 API の範囲であれば動く見込みは高いですが、新 API は使えません。また、ストア提出の最新 SDK 要件を跨ぐと更新が出せなくなります。 <dt>Q. .NET 10 RC を本番投入しても大丈夫?</dt> <dd>A. RC は品質が高いものの、<strong>仕様の最終確定前</strong>です。本番は GA 待ちを推奨。どうしても先行したい場合は <strong>機能フラグ+段階配布+即時ロールバック</strong>の体制を整えてください。</dd> <dt>Q. Xcode は 1 台の Mac に 2 つ入れて切り替えても良い?</dt> <dd>A. 可能ですが、<strong>キャッシュ・ツールチェーンの競合</strong>で不安定化しやすいです。安定運用を優先するなら、<strong>ビルドマシンを分離</strong>しましょう。</dd> <dt>Q. ネイティブ連携はどの程度コストがかかる?</dt> <dd>A. ラップ対象 API によりますが、小規模機能であれば <strong>ラッパ(Swift)+バインディング</strong>で数日〜数週間。GA 後に公式バインディングへ置き換え可能な設計にしてください。</dd> </dl> 最終提言(要点まとめ) 正式サポートは .NET 10 から。RC は検証用として積極活用、本番は GA 待ち。 .NET 9 は Xcode 25 で固定。OS 26 端末での利用自体は多くのケースで可能だが、新 API は不可。 移行は 3 段階:現行維持 → RC で検証 → GA で本番移行。依存パッケージの対応状況を必ず棚卸し。 CI/CD 分離が肝。新旧の Xcode/SDK を混在させない、Xcode は xcode-select で固定。 必要ならネイティブでフォールバック。GA 後に置き換える設計で、OS 26 の価値を先取り。 以上を踏まえると、「現行プロダクトは .NET 9 + Xcode 25 で維持し、.NET 10 正式版が出次第、短期で段階的に移行」が、最も安全かつ工数の少ない対応方針です。 付録:現場でそのまま使えるメモ集 Xcode/SDK 関連コマンド # Xcode パスの確認/切替 xcode-select -p sudo xcode-select -switch /Applications/Xcode_25.app # Command Line Tools の確認 xcode-select --install # SDK/ツールのバージョン xcodebuild -version xcodebuild -showsdks <h3>.NET/MAUI 関連コマンド</h3> <pre><code class="language-bash"># ワークロードの確認・更新 dotnet workload list dotnet workload update # パッケージの棚卸し dotnet list package --outdated --include-transitive # iOS 向けビルド/アーカイブ dotnet build -c Release -f net9.0-ios dotnet publish -c Release -f net10.0-ios -p:UseNet10=true -p:ArchiveOnBuild=true

<h3>実行時の OS 判定(C#)</h3>
<pre><code class="language-csharp">if (OperatingSystem.IsIOSVersionAtLeast(26))

{ // iOS 26 以降 } if (OperatingSystem.IsMacOSVersionAtLeast(26)) { // macOS 26 以降(仮) }

※ バージョン番号はプロジェクトで採用している命名・マッピングに合わせて調整してください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次