背景與問題現象
既有 .NET MAUI 應用用 Xcode 27 建置、安裝到搭載 iOS 27 的 iPhone 上,能正常顯示啟動畫面,接著就直接消失,這種「盛大登場卻無疾而終」的症狀可能來自很多原因。但如果唯一改變的只有建置工具從舊版換成 Xcode 27、執行環境換成 iOS 27,而且裝置日誌明確出現「UIScene life cycle is required for apps built with this SDK」這一行訊息,就代表問題出在應用仍只使用舊的應用程式生命週期。這個判斷依據來自微軟 .NET 官方部落格 devblogs.microsoft.com 上、由 .NET MAUI 團隊資深軟體工程師 Gerald Versluis 撰寫的文章,標題為 Get Your .NET MAUI App Ready for Xcode 27。
核心概念:UIScene 生命週期
UIKit 的 UIScene 生命週期負責管理應用程式每一次執行個體的使用者介面,包含視窗本身以及前景與背景之間的切換;它與負責整個應用程式行程的應用程式生命週期並行運作。蘋果在 iOS 13 引入這套模式,而 .NET MAUI 本身早已支援,所以這次的問題屬於 Xcode 27 的相容性要求,不是 .NET MAUI 生命週期壞掉。既有專案之所以仍在用舊模式,原因是升級 .NET MAUI 或其 workloads 並不會主動改寫專案裡的平台層檔案,這些檔案需要開發者自行補上。另外這也不是某款特定裝置(例如文中提到的 iPhone Duo)的問題:任何用 Xcode 27 建置的 iOS 應用、任何外形尺寸都需要改用這個模式,反之用舊 SDK 建置的既有二進位檔,不會因為使用者更新手機就突然需要遷移。
手動遷移的核心:兩個檔案
對一般應用來說,改動其實很小:一個 scene delegate,加上 Info.plist 的一筆設定。具體做法是在 Platforms/iOS/SceneDelegate.cs 建立繼承 MauiUISceneDelegate 的 SceneDelegate 類別,加上 [Register("SceneDelegate")] 註冊,類別維持 public,而 .NET MAUI 會代為完成視窗建立;原本的 AppDelegate:MauiUIApplicationDelegate 原封不動保留,因為 scene delegate 是與它並肩運作,不是取代它。接著在 Platforms/iOS/Info.plist 的根 dict 加入 UIApplicationSceneManifest,其中三個細節必須精準:設定名稱 MAUI_DEFAULT_SCENE_CONFIGURATION 必須一字不差,這是 .NET MAUI 預期的名稱;plist 裡的 UISceneDelegateClassName 要與 C# 的註冊名稱一致;UIApplicationSupportsMultipleScenes 維持 false,除非應用真的支援多視窗。不需要 UISceneStoryboardFile,因為視窗是用程式碼建立,既有的啟動畫面設定也要原樣保留。若專案同時支援 Mac Catalyst,就在 Platforms/MacCatalyst 下重複同樣的兩項變更。另外,有些應用會依 Debug、Release、客戶或環境建置挑選不同的 plist,每一份可能產出正式版的設定檔都要補上,只改預設檔案時,仍可能有一種組態在啟動畫面後當機。
工具鏈與版本檢查
動手前應先確認實際使用的專案、.NET SDK、.NET MAUI 套件、Apple workloads 與 Xcode 版本,global.json、共用的套件版本或既有的自訂 scene delegate 都會改變正確作法。可用 maui doctor 快速檢查作用中的 SDK、workload、選定的 Xcode 與 Apple SDK 相容性,連同 Xcode 授權、命令列工具與已安裝的 iOS runtime 也會一併檢查。撰文當下,已發布的 .NET 10 iOS 與 Mac Catalyst workloads 27.0.10722 要求 Xcode 27.0,安裝更新的 Xcode 並不會自動變得相容,應查閱發布說明搭配正確版本,而不是關閉 SDK 的 Xcode 檢查。對 .NET 10 應用而言,啟用 scene 之前要把 .NET MAUI 套件升到 10.0.110 以上:兩個檔案的啟動修復在舊版也能生效,但 10.0.110 才包含 scene 端的 App Actions 派發與相關 WebAuthenticator 轉送,若只修啟動而未升套件,可能默默弄壞這些整合。用 10.0.110 範本新建的專案已具備 scene 設定,既有專案則不會因為升套件就被回填。最低作業系統版本同時被拉高:iOS 至少 15.0,Mac Catalyst 至少 17.0(macOS 14 Sonoma),應在 csproj 的 SupportedOSPlatformVersion 改既有值而非新增重複項目;若必須繼續支援 iOS 15 以下或 macOS 12、13,就需要做支援政策決策,期間留在相容的 26.x SDK,不要默默放棄這些使用者。
讓代理處理遷移的選項
文章也介紹 maui-xcode27-migration 這項 agent skill,它是一組給程式碼代理的指示與資源,不是應用的執行期相依項。安裝需要 Microsoft.Maui.Cli 0.1.0-preview.13.26512.5 或更新版本,CLI 會從公開的 dotnet/maui-labs 目錄下載 skill,因此需要網路連線;從應用程式儲存庫執行 maui ai list skill 與 maui ai add skill maui-xcode27-migration --env CopilotCli --yes,或改用 GitHub Copilot 的 dotnet-maui 外掛,兩者擇一即可,避免裝出重複副本。skill 會先判斷版本相容性、補齊 iOS 與 Mac Catalyst 的 scene 設定(含各組態專屬 plist)、保留既有 scene 自訂、啟動畫面與多視窗行為,再逐一檢查 app-delegate 覆寫、生命週期註冊、驗證登入、深層連結、通知點擊、需要視窗的啟動工作與 App Actions,最後跑唯讀的生命週期審查、檢查 diff、建置並測試。CLI 與 skill 屬實驗性質,不在 Microsoft 支援政策涵蓋範圍內,而且安裝 skill 本身不會動到應用。
既有自訂行為的轉移
啟用 scene 之後,UIKit 不會再呼叫 AppDelegate 的激活、背景化、URL 與 user-activity 回呼,ConfigureLifecycleEvents 註冊與 OpenUrl、ContinueUserActivity 等覆寫都失效,必須改為對應的 scene 事件,例如 OnActivated 改 SceneOnActivated、OpenUrl 改 SceneOpenUrl、ContinueUserActivity 改 SceneContinueUserActivity。回呼參數不同,所以是搬邏輯而非改名;若改為在 SceneDelegate 覆寫,則要呼叫對應的基底實作,框架的視窗建立、生命週期事件與驗證轉送都依賴該行為。自訂深層連結還需處理 SceneWillConnect,因為啟動用的 URL 與 user activity 位於 UISceneConnectionOptions,該回呼在 .NET MAUI 建立視窗之前執行,導航要延後到視窗就緒;通知點擊則看 connectionOptions.NotificationResponse,而一般啟動可能沒有任何 payload,列舉前要先防呆。FinishedLaunching 仍會執行,但視窗改由 scene delegate 較晚建立,AppDelegate.Window 會是 null,原本在 base.FinishedLaunching 之後取用它的程式必須改用 scene 視窗。跨平台的 Window 生命週期事件仍有效,不要重複轉送。App Actions 方面,每個 PerformActionForShortcutItem 處理器都要恰好呼叫一次 completion,處理了傳 true、沒處理傳 false,僅寫日誌也要回應;.NET MAUI 10.0.110 已把 App Actions 轉給 Essentials,不需再自行註冊一筆。
驗證與限制
還原並建置用 dotnet restore -p:TargetFramework=net10.0-ios 與 dotnet build -f net10.0-ios --no-restore,模擬器若還像舊版本就先解除安裝並清掉 bin、obj。要在搭載 iOS 27 的一般 iPhone 或 iPad 模擬器上同時測 Debug 與 Release:在 iOS 26 建置成功不代表用 Xcode 27 建置後能在 iOS 27 啟動,必須實測冷啟動、前後景切換、驗證登入流程、連結與通知點擊(應用執行中與完全關閉兩種狀態)、App Actions,若支援 Mac Catalyst 也要一併驗證。建置成功並不等於驗證重導向或主畫面捷徑還能用。文末留言提供了一個應急作法:在 csproj 加入 ValidateXcodeVersion false 可先解除版本檢查封鎖,但作者提醒這可能引進難以追蹤的當機,因為會用到與當前 Xcode 不符的繫結,僅適合開發期暫時使用。裝置專屬版面與 Xcode 27.1 工具鏈是另一個主題,將由後續文章處理。
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。