DEV Community

Dan Ardelean
Dan Ardelean

Posted on AI-assisted

Why is my .NET MAUI iOS application crashing at launch after upgrading to Xcode 27?

Apple now requires the UIScene lifecycle. Most .NET MAUI apps created before September 2026 don't have it. Here is what breaks, who is affected, and what I had to change in two production apps beyond the "official" fix.


Last week I rebuilt two production .NET MAUI apps on a Mac that had just been upgraded to Xcode 27. Both compiled without a single warning. Both died a fraction of a second after the splash screen on every iOS 27 device and simulator, in Debug and in Release.

No managed exception. No UnhandledException event. Visual Studio didn't even notice. The only trace was a native crash report on the Mac:

Exception Type:  EXC_BREAKPOINT (SIGTRAP)

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0  UIKitCore  ___UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption_block_invoke + 712
1  libdispatch.dylib  _dispatch_client_callout + 12
2  libdispatch.dylib  _dispatch_once_callout + 28
3  UIKitCore  -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 320
Enter fullscreen mode Exit fullscreen mode

The function name says it all: NoSceneLifecycleAdoption. If you ship .NET MAUI apps for iOS, this article is for you.

What Apple changed, and when

Since iOS 13, UIKit has had two application lifecycles:

  • the legacy lifecycle, where UIApplicationDelegate owns the single UIWindow and receives every callback (didFinishLaunching, openURL, applicationDidBecomeActive, and so on);
  • the scene lifecycle, where the app delegate only bootstraps the process and a UISceneDelegate owns each window (a "scene") and receives the UI-related callbacks.

For seven years the legacy path kept working. Apple escalated in three steps, all documented in Technote TN3187, "Migrating to the UIKit scene-based life cycle":

  • iOS 18.4: UIKit logs "This process does not adopt UIScene lifecycle. This will become an assert in a future version."
  • iOS 26: the message becomes "UIScene lifecycle will soon be required. Failure to adopt will result in an assert in the future." At WWDC25 (session 282, "Make your UIKit app more flexible") Apple said it out loud: "In the next major release following iOS 26, UIScene life cycle will be required when building with the latest SDK."
  • iOS 27 SDK, Xcode 27: the assert is live. The iOS and iPadOS 27 release notes put it in one sentence: "Apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch."

Two details matter in practice:

  • The trigger is the SDK you link against, not the OS the user runs. An Apple engineer confirmed it on the developer forums: "The assert is only evaluated when building with the next major release's SDK." Your existing binary built with Xcode 26 keeps working on iOS 27 phones. Rebuild it with Xcode 27 and it dies.
  • The assert fires on iOS 27 devices and simulators only, and on macOS 27 for Mac Catalyst apps. The same Xcode 27 build still launches on iOS 26. That is why a quick test on an older phone, or an App Store review run on an older device, can pass. The dotnet/macios issue linked below reports exactly that: an app without scenes approved by App Review, then crashing for users on iOS 27.

Apple also says that only adoption of the scene lifecycle is required. Supporting multiple windows is encouraged but optional, and that distinction becomes important in the Info.plist below.

Is the stock .NET MAUI template affected?

Yes, unless your workload is recent enough. I checked the actual template packages on NuGet rather than trusting what my machine generates:

  • Microsoft.Maui.Templates.net10 10.0.100 and earlier: no SceneDelegate.cs, no UIApplicationSceneManifest in Info.plist. Affected.
  • Microsoft.Maui.Templates.net10 10.0.110, the .NET 10 SR11 release of September 22, 2026: includes Platforms/iOS/SceneDelegate.cs, Platforms/MacCatalyst/SceneDelegate.cs and the scene manifest in every Apple head of every template (maui, maui-blazor, maui-blazor-web, maui-multiproject). Fixed.
  • Microsoft.Maui.Templates.net11 11.0.0-rc.1, the latest .NET 11 prerelease at the time of writing: still without scenes. In fairness, .NET 11 RC 1 ships iOS 26.5 bindings and requires Xcode 26.6, so you cannot build it with Xcode 27 anyway. The fix is already in the release/11.0.1xx-rc2 branch of dotnet/maui. I diffed that branch's maui, maui-blazor, maui-blazor-web and maui-multiproject templates against 10.0.110: the iOS and Mac Catalyst Info.plist, SceneDelegate.cs, AppDelegate.cs and Program.cs are identical, so everything in this article applies unchanged to .NET 11 projects. RC 2 will generate the right files; the final .NET 11 is documented as requiring Xcode 27.

The template change is dotnet/maui pull request #38601, "Adopt scene lifecycle and AppActions dispatch", merged on September 18, 2026 and backported to SR11 the same day. Its description is explicit about the root cause: the generated app compiled fine but terminated after the splash screen with "Application failed to launch: UIScene life cycle is required for apps built with this SDK."

The timeline explains why so many people hit this in the same week. Xcode 27 shipped mid-September. MAUI 10.0.110 with the new template came out on September 22. Official Xcode 27 support for .NET for iOS (workload set 10.0.401.1) arrived on September 29. From that day on, everyone who updated their Mac rebuilt existing apps with the iOS 27 SDK, and existing apps don't get new template files.

That is the rule to remember: the version of the template that created your project is what matters, not the MAUI package you reference today. Upgrading Microsoft.Maui.Controls to 10.0.110 does not touch your Info.plist. A reporter in dotnet/maui issue #38992 had exactly this setup, 10.0.110 package and a black screen at launch, and fixed it by adding the two files by hand. And no, the build does not inject the manifest for you: neither the .NET for iOS SDK nor MAUI synthesizes it. The only switch in the whole stack is HasSceneManifest(), which just checks whether the key exists in the compiled Info.plist.

A quick way to check which template you have:

dotnet workload list
dotnet new maui -n Probe -o /tmp/Probe && ls /tmp/Probe/Platforms/iOS
Enter fullscreen mode Exit fullscreen mode

If SceneDelegate.cs is in the listing, your template is current. If not, run dotnet workload update before creating new projects. Visual Studio 2026 may bundle an older workload than the .NET SDK on your Mac; the dotnet/macios issue below was opened because the VS "New Project" dialog still produced the old layout.

For comparison, the native .NET for iOS template (dotnet new ios) adopted scenes in July 2025, fourteen months earlier, right after WWDC25. The MAUI template waited for the crash.

Reproducing it in five minutes

I took the 10.0.110 template, which works, and removed the two scene pieces to get a "legacy" app. Then I built both for the iOS 27 simulator with Xcode 27.0 (27A266a) and .NET SDK 10.0.401:

  • the untouched template launches and shows the usual "Hello, World!" page;
  • the legacy copy is killed at launch, with the stack trace above, in both Debug and Release;
  • the same pair built for Mac Catalyst with the macOS 27.0 SDK behaves identically on macOS 27.0.1: the template runs, the legacy copy crashes with the same trap.

I specifically tested the hypothesis that only Release builds crash. It is false: the Debug build produced a byte-for-byte identical crash report. I also attached lldb to the Debug build before launch, in case the check behaved differently under a debugger. It doesn't. The process stops on a literal brk #0 instruction inside UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1c48c336c)
    frame #0: UIKitCore`___UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption_block_invoke + 712
->  0x1c48c336c <+712>: brk    #0
Enter fullscreen mode Exit fullscreen mode

What changes between Debug and Release is only how visible the failure is. The IDE often reports nothing useful because the trap happens on the native side before any managed frame is involved. On the simulator, simctl launch even returns a PID, and the process is gone a moment later.

The fix

Two files. No NuGet update required, because MauiUISceneDelegate has been part of Microsoft.Maui since 2021, when multi-window support for iPad and Mac Catalyst was added. Apple's requirement just turned an opt-in feature into the only path.

1. Add Platforms/iOS/SceneDelegate.cs

using Foundation;

namespace YourApp;

[Register("SceneDelegate")]
public class SceneDelegate : MauiUISceneDelegate
{
}
Enter fullscreen mode Exit fullscreen mode

The class can stay empty. MauiUISceneDelegate already creates the MAUI window in WillConnect and forwards the scene callbacks (sceneDidBecomeActive, sceneWillEnterForeground, and the rest) to the cross-platform Window lifecycle events. Do not add your own forwarding; you would get every event twice.

2. Declare the scene in Platforms/iOS/Info.plist

<key>UIApplicationSceneManifest</key>
<dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <false/>
    <key>UISceneConfigurations</key>
    <dict>
        <key>UIWindowSceneSessionRoleApplication</key>
        <array>
            <dict>
                <key>UISceneConfigurationName</key>
                <string>__MAUI_DEFAULT_SCENE_CONFIGURATION__</string>
                <key>UISceneDelegateClassName</key>
                <string>SceneDelegate</string>
            </dict>
        </array>
    </dict>
</dict>
Enter fullscreen mode Exit fullscreen mode

Three things here are not arbitrary:

  • __MAUI_DEFAULT_SCENE_CONFIGURATION__ is a magic string. MauiUIApplicationDelegate.GetConfiguration returns a UISceneConfiguration with exactly this name, and MauiUISceneDelegate.WillConnect only creates the window when the session configuration has this name. Rename it and you get a black screen instead of a crash.
  • UISceneDelegateClassName must match the [Register("SceneDelegate")] attribute, not the C# class name or namespace.
  • UIApplicationSupportsMultipleScenes is false in the official template. The Microsoft Learn page on multi-window, which until now was the only place showing this snippet, sets it to true, and so do most snippets you will find online. true works, but it also tells iPadOS and Mac Catalyst that your app supports multiple windows, which changes the app switcher, split view and window tabbing on the Mac. Keep it false unless you actually want multi-window.

Then delete bin and obj and rebuild. This is not superstition: the merged Info.plist is cached, and several reporters saw the fix "not work" until they cleaned. Program.cs does not change: UIApplication.Main(args, null, typeof(AppDelegate)) is still the entry point, and AppDelegate is still needed because it hosts the MauiApp and answers UIKit's scene configuration request. It just no longer owns the window.

3. Mac Catalyst, if you target it

Mac Catalyst is affected in exactly the same way, not only by the deployment-target change below. Catalyst apps are UIKit apps, and the requirement is UIKit's. I built the no-scene app for Mac Catalyst with the macOS 27.0 SDK and launched it on macOS 27.0.1: it dies immediately with the same EXC_BREAKPOINT in UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption, while the untouched 10.0.110 template runs.

So the same two pieces go into Platforms/MacCatalyst: a SceneDelegate class and the manifest in that head's Info.plist. The Mac Catalyst 27 SDK also refuses SupportedOSPlatformVersion 15.0 for app projects; the template now uses 17.0.

While you are in the plist: the 27 SDK also makes a launch screen mandatory (UILaunchStoryboardName or one of its siblings). MAUI's MauiSplashScreen item emits it for you, but if you ever removed the splash screen configuration, that is a second way to fail at launch.

What MAUI does under the hood

It helps to know why the fix is this small. MauiUIApplicationDelegate.FinishedLaunching looks at the bundle:

// if there is no scene delegate or support for scene delegates, then we set up the window here
if (!this.HasSceneManifest())
{
    this.CreatePlatformWindow(_application, application, launchOptions);
    ...
}
Enter fullscreen mode Exit fullscreen mode

With no manifest, the app delegate creates the window itself (legacy lifecycle). With the manifest present, it answers application:configurationForConnectingSceneSession:options: with the MAUI configuration name, UIKit instantiates your SceneDelegate, and the window is created in WillConnect. Your cross-platform App, Window and page code is identical in both paths.

The consequence, and the source of every gotcha below, is that the window now exists later and is owned by a different object, and UIKit delivers several events to that object instead of the app delegate.

Gotchas from two production apps

The empty SceneDelegate is enough for the template. It was not enough for either of my apps. These are the things that broke after the two-file fix compiled.

Gotcha 1: AppDelegate.OpenUrl is never called again

The first app is a family of public-transport apps that sign in through Microsoft Entra with MSAL. The login happens in a system browser and returns to the app through a custom URL scheme (msauth.<bundle id>://). Before the change, the return URL was handled here:

// AppDelegate.cs, before
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
    AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
    return base.OpenUrl(app, url, options);
}
Enter fullscreen mode Exit fullscreen mode

With a scene manifest, UIKit routes incoming URLs to scene:openURLContexts: on the scene delegate. The app delegate override is simply never invoked, so the login flow hangs forever in the browser. The fix is to move the handler, with the scene-flavored signature that receives a set of UIOpenUrlContext:

// SceneDelegate.cs, after
[Register("SceneDelegate")]
public class SceneDelegate : MauiUISceneDelegate
{
    public override bool OpenUrl(UIScene scene, NSSet<UIOpenUrlContext> urlContexts)
    {
        foreach (var context in urlContexts)
            AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(context.Url);

        return base.OpenUrl(scene, urlContexts);
    }
}
Enter fullscreen mode Exit fullscreen mode

The same applies to anything else that used to arrive through the app delegate's URL or user-activity methods: deep links, universal links (ContinueUserActivity), OAuth callbacks for Google or Apple Sign In, payment return URLs. Grep for every override and move it. If you use the lifecycle events API instead of overrides, the mapping is OpenUrl to SceneOpenUrl and ContinueUserActivity to SceneContinueUserActivity.

There is a second half to this that bit me only later: a cold start from a URL or a universal link no longer puts the URL in FinishedLaunching's launch options. It arrives in connectionOptions.UrlContexts and connectionOptions.UserActivities inside WillConnect. The same is true for a cold launch from a push notification tap, which now lives in connectionOptions.NotificationResponse. Warm delivery and cold delivery are two different code paths, and you need to test both.

One more trap in that project: each app flavor (Dev, Stage, Prod) has its own Info.plist selected by build configuration. The manifest had to go into all of them. A missing one gives you an app that crashes only in one configuration, which is a miserable thing to debug on a Friday.

Gotcha 2: startup code that assumed a window in FinishedLaunching

The second app is a field-service app with a custom bootstrap: after FinishedLaunching, the app ran an async RunAsync() that initialized databases and services and then navigated to the first page. Simplified:

// AppDelegate.cs, before
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    var result = base.FinishedLaunching(app, options);
    InitApplicationAsync();   // eventually sets the root page
    return result;
}
Enter fullscreen mode Exit fullscreen mode

In the legacy lifecycle this was fine because base.FinishedLaunching had already created the window. With scenes, FinishedLaunching returns before any window exists; the window is created in SceneDelegate.WillConnect, which runs afterwards. The bootstrap now raced against window creation and the navigation landed on a window that wasn't there yet.

The fix is to start the application from the scene, once:

// SceneDelegate.cs, after
[Register("SceneDelegate")]
public class SceneDelegate : MauiUISceneDelegate
{
    private static bool _started;

    public override void WillConnect(UIScene scene, UISceneSession session,
                                     UISceneConnectionOptions connectionOptions)
    {
        base.WillConnect(scene, session, connectionOptions);   // the window is created here

        if (_started)
            return;

        _started = true;
        RunApplicationAsync();
    }

    private static async void RunApplicationAsync()
        => await AppBootstrapper.Instance.RunAsync();
}
Enter fullscreen mode Exit fullscreen mode

AppDelegate.FinishedLaunching keeps only the process-level work: SQLite initialization and the global exception handlers. The rule of thumb: anything that touches Window, MainPage, navigation or UIApplication.SharedApplication.KeyWindow belongs after WillConnect, not in FinishedLaunching. With the lifecycle events API, OnPlatformWindowCreated and SceneWillConnect are the hooks you want. And while you are there, replace KeyWindow, deprecated since iOS 13, with WindowStateManager.Default.GetCurrentUIWindow() or the UIWindowScene.KeyWindow of a connected scene.

Gotcha 3: everything else that was wired to the app delegate

After the two apps launched again, I went through the remaining AppDelegate overrides. Scene-based apps also stop receiving these on the app delegate:

  • Home screen quick actions (PerformActionForShortcutItem). UIKit does not call the app delegate selector for scene apps. MAUI 10.0.110 added the scene-side handler and forwards it to the existing iOSLifecycle.PerformActionForShortcutItem registrations, so if you use ConfigureLifecycleEvents and are on 10.0.110 you are fine. If you overrode the app delegate method, or you are on an older MAUI and added the manifest by hand, quick actions silently stop working. There is a new contract to respect, too: every registered handler must now invoke its completion callback exactly once, true if handled and false otherwise, including logging-only observers. A handler that never acknowledges blocks the native completion, and there is no timeout.
  • Foreground and background transitions. OnActivated, OnResignActivation, DidEnterBackground and WillEnterForeground still fire on the app delegate at process level, and MAUI's Window.Activated, Deactivated, Stopped and Resumed keep working because MAUI switches its own forwarding to the scene events when a manifest is present. But UI work such as adding a privacy blur over the window belongs in the scene equivalents (sceneDidBecomeActive, sceneWillResignActive), where Window is guaranteed to exist.

Push notification registration and DidReceiveRemoteNotification stay on the app delegate. Nothing to move there, except the cold-launch case mentioned above.

The upgrade path, end to end

The scene fix is the visible part. Getting a team and a CI pipeline onto Xcode 27 without a broken week has a few more steps, and the order matters.

Step 0: adopt scenes before you touch the toolchain

The scene lifecycle has worked since iOS 13, and MAUI has supported it since 2021. Nothing in the fix depends on Xcode 27. So the safest sequence is to add SceneDelegate and the manifest while you are still building with Xcode 26, ship that to production, watch the login and deep-link flows for a release cycle, and only then move the toolchain. When Xcode 27 arrives, your app is already compliant and the upgrade becomes a non-event. Both of my apps would have been in this situation if I had read TN3187 in June instead of a crash report in October.

Step 1: know your target versions

As of October 2026, the supported stack for building MAUI apps with the iOS 27 SDK is one specific combination, published in the dotnet/macios release ".NET 10 - Xcode 27.0 support" of September 29:

  • macOS 26.6 or later (Xcode 27 does not install on older systems).
  • Xcode 27.0.
  • .NET SDK 10.0.401.
  • Workload set 10.0.401.1, which brings the iOS, tvOS, macOS and Mac Catalyst SDKs 27.0.10722 and MAUI workload 10.0.110.
  • Microsoft.Maui.Controls 10.0.110 or later, recommended but not strictly required. Both of my apps still reference 10.0.90 and run fine with the manual fix; what 10.0.110 adds on top is the scene-side handling of quick actions and the WebAuthenticator callbacks described above.

Everything else is out:

  • .NET 9 and .NET 8 apps cannot build with Xcode 27. The last .NET 9 release from dotnet/macios targets Xcode 26.5 (May 2026), and .NET 9 left support the same month. There is no .NET 8 or .NET 9 release for Xcode 27 and none is coming. If your app is still on .NET 8 or 9, the Xcode 27 migration is really a .NET 10 migration with the scene fix on top.
  • .NET 11 RC 1 requires Xcode 26.6, not 27. Do not move to .NET 11 to solve this problem. RC 2 ships the scene template and the final release in November is documented as requiring Xcode 27, but today the only supported way to build against the iOS 27 SDK is .NET 10. When you do move to .NET 11 later, note that its template also raises the Android minimum to API 24; unrelated to scenes, but part of the same upgrade.

Step 2: upgrade in the right order on the Mac

The trap here is macOS. Xcode 26.x does not run on macOS 27, so if you take the macOS 27 update before the .NET side is ready, you lose the ability to build with the old SDK. The dotnet/macios maintainers said it in the Xcode 27 tracking issue, two weeks before the release: don't update to macOS 27 until support ships. That window is now closed, but the same logic applies to every future major release: .NET support first, then Xcode, then macOS.

On a Mac that is still on macOS 26:

# 1. .NET SDK 10.0.401 from dotnet.microsoft.com, then verify
dotnet --version            # must print 10.0.401

# 2. Install Xcode 27 from the App Store or developer.apple.com, then select it
sudo xcode-select -s /Applications/Xcode.app
xcodebuild -version         # Xcode 27.0

# 3. Pin the workload set that matches Xcode 27
dotnet workload install maui ios maccatalyst --version 10.0.401.1
dotnet workload --info      # shows workload version 10.0.401.1
Enter fullscreen mode Exit fullscreen mode

Pinning the workload set with --version is worth the extra flag. A plain dotnet workload update takes whatever is latest, which is fine today and a surprise the day a new set lands mid-sprint. Pin the same version in global.json with "sdk": { "version": "10.0.401", "rollForward": "latestPatch" } so every machine and every CI agent resolves the same toolchain.

Then verify the templates, not just the workload. The dotnet new template cache under ~/.templateengine can keep serving an older MAUI template after the workload is updated. This is not a hypothetical: in the pull request that validated the .NET 11 RC 2 templates, the MAUI team's own first local run generated apps "from stale non-scene user templates" while the package versions looked correct. Generate a throwaway project and check for SceneDelegate.cs; if it is missing, clear the template cache and try again:

dotnet new maui -n Probe -o /tmp/Probe && ls /tmp/Probe/Platforms/iOS
# no SceneDelegate.cs?  ->  rm -rf ~/.templateengine && dotnet new maui -n Probe -o /tmp/Probe --force
Enter fullscreen mode Exit fullscreen mode

If you already updated to macOS 27 and need an emergency build with the old SDK, there are community workarounds (running the Xcode 26.6 bundle anyway and selecting it with xcode-select, or setting ValidateXcodeVersion=false in the project). I have not tested them, and they are exactly the kind of thing you don't want on the release path. Shipping the scene fix with Xcode 27 is less work.

Step 3: per project

  1. Add the scene files and the manifest to every iOS and Mac Catalyst head, including every flavor-specific Info.plist.
  2. Raise SupportedOSPlatformVersion for Mac Catalyst app projects to 17.0. iOS can stay at 15.0, the template default. The Catalyst change is a real deployment-target drop: 17.0 means macOS 14 (Sonoma) or later. If you still need to support macOS 12 or 13 users, the Templates README is blunt about it: stay on the 26.x SDK and keep the lower target, which also means staying off Xcode 27 for that app.
  3. Update Microsoft.Maui.Controls and Microsoft.Maui.Controls.Compatibility to 10.0.110 if you can. If you use Microsoft.Maui.Essentials features like AppActions or WebAuthenticator, you should.
  4. Delete bin and obj. Yes, again.
  5. Build Debug and Release for the iOS 27 simulator and launch both. Then run the authentication, deep-link and notification flows, warm and cold.

Step 4: CI and the rest of the team

  • Pin the Xcode version on your build agents. On hosted macOS images it means selecting the Xcode 27 image or running sudo xcode-select -s /Applications/Xcode_27.0.app as a first step, and installing the workload set by version, not by "latest".
  • Add an iOS 27 simulator to whatever UI or smoke test you run. An iOS 26 simulator will keep passing on a build that crashes on every customer's phone.
  • Windows developers using Visual Studio 2026 with a paired Mac: the first Xcode 27 release had a known issue where the remote iOS simulator loops on "Connecting to Mac" until a Visual Studio update ships. Check the dotnet/macios tracking issue #25660 before you tell the whole team to update.
  • Communicate the trigger clearly: it is the SDK on the build machine, not the OS on the phone. A developer who updates their Mac and rebuilds is now producing crashing binaries for everyone on iOS 27, even if nothing in the repository changed.

A checklist you can paste into a ticket

  1. Toolchain: macOS 26.6 or later, Xcode 27.0, .NET SDK 10.0.401, workload set 10.0.401.1, pinned in global.json and in CI. Apps still on .NET 8 or 9 move to .NET 10 first.
  2. Add Platforms/iOS/SceneDelegate.cs deriving from MauiUISceneDelegate, registered as SceneDelegate.
  3. Add UIApplicationSceneManifest to every iOS Info.plist in the project, with __MAUI_DEFAULT_SCENE_CONFIGURATION__ and UIApplicationSupportsMultipleScenes = false.
  4. Same for Mac Catalyst, plus SupportedOSPlatformVersion 17.0.
  5. Move OpenUrl and ContinueUserActivity overrides from AppDelegate to SceneDelegate, and handle cold-start URLs, activities and notification responses from connectionOptions in WillConnect.
  6. Move any startup code that needs the window from FinishedLaunching to after base.WillConnect.
  7. Review quick-action and foreground/background handlers.
  8. Delete bin and obj, rebuild, and test on an iOS 27 simulator or device. An iOS 26 device will not show the problem.
  9. Test the login, deep-link and notification-tap flows explicitly, warm and cold. They compile fine when broken.

References

  • Apple, Technote TN3187, Migrating to the UIKit scene-based life cycle. The timeline, the migration table from UIApplicationDelegate to UISceneDelegate, and the "should I migrate" criteria.
  • Apple, iOS & iPadOS 27 Release Notes, UIKit deprecations: "Apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch."
  • Apple, WWDC25 session 282, Make your UIKit app more flexible.
  • Apple Developer Forums, threads 788293 and 789004, where an Apple engineer confirms the assert applies only when building with the new SDK.
  • dotnet/macios issue #26837, "[PSA] Starting with Xcode 27 (and iOS 27) you MUST use the Scene life cycle". Community write-up with the crash report and the manual fix.
  • dotnet/macios release .NET 10 - Xcode 27.0 support, which recommends MAUI 10.0.110 or later.
  • dotnet/maui pull request #38601, "[iOS/MacCatalyst] Adopt scene lifecycle and AppActions dispatch", and its SR11 backport #38664.
  • dotnet/maui issue #38992, an existing app on MAUI 10.0.110 still failing until the files were added by hand.
  • dotnet/maui Templates README, section "iOS and Mac Catalyst scene lifecycle". Currently the most complete migration note from the MAUI team.
  • Microsoft Learn, .NET MAUI Window, iPadOS and macOS configuration and App lifecycle, iOS and Mac Catalyst. Both still describe scenes as a multi-window option and do not yet mention that adoption is mandatory.

Tested with Xcode 27.0 (27A266a), .NET SDK 10.0.401, MAUI workload 10.0.110, iOS 27.0 simulator, and Mac Catalyst on macOS 27.0.1. The two production fixes described here were committed on October 5, 2026.

This article was co-authored with Claude (Anthropic), which helped reproduce the crash on the iOS 27 simulator, trace the fix through the .NET MAUI source and templates, and collect the references.

Top comments (0)