Yesterday, 09:15 PM
Your existing .NET MAUI app builds with Xcode 27, installs on an iPhone running iOS 27, shows its splash screen, and then disappears. Not exactly the grand entrance you were hoping for.
That symptom can have many causes. But if the only thing that changed is that you’re now building with Xcode 27 and running the app on iOS 27, and you see the device log message below, your .NET MAUI app needs a few changes:
UIKit’s scene lifecycle manages each instance of an app’s user interface, including its windows and transitions between foreground and background states. It works alongside the application lifecycle, which manages the app process as a whole. Apple introduced this model with iOS 13, and .NET MAUI already supports it.
This is an Xcode 27 compatibility requirement, not a broken .NET MAUI lifecycle. Existing .NET MAUI projects might still use only the older application lifecycle because updating .NET MAUI or its workloads doesn’t rewrite their platform files. It’s not specific to the iPhone Duo. Every iOS app built with Xcode 27, on any form factor, needs to adopt this pattern. An existing binary built with an older SDK doesn’t suddenly need this migration just because someone updates their phone.
For a straightforward app, the change is small: a scene delegate and an entry. The part that deserves your attention is everything you’ve added over the years, such as authentication callbacks, deep links, and App Actions.
That’s where the agent skill comes in. It guides your coding agent through the project checks, the migration, and the verification. You still review the changes, and the manual steps below explain what it’s doing.
Let an AI agent handle the migration
The .NET MAUI CLI can install agent skills into your development environment. A skill is a set of instructions and supporting resources for the agent. It’s not another runtime dependency in your app.
The Xcode 27 migration skill requires Microsoft.Maui.Cli 0.1.0-preview.13.26512.5 or later. Install that preview release:
If you already have it installed, update it instead:
Preview tooling
The .NET MAUI CLI and this skill are experimental and are not covered by the Microsoft Support Policy. The CLI downloads the skill from the public catalog, so catalog discovery and installation require network access.
From your app’s repository, check that the CLI can find the skill and install it for GitHub Copilot CLI:
If you use another AI provider, check and change the option accordingly. Run the command from the app repository you want to change, because the skill is installed for that project.
Restart or reload your agent session so it picks up the new skill, then ask:
You don’t need to name the skill in your request. A compatible agent should pick from the installed skills based on the task. If you want to remove any doubt, feel free to name it explicitly.
The CLI installs the skill, and your coding agent does the migration. There’s no command in this workflow, and installing the skill on its own doesn’t touch your app.
Prefer to manage the skill directly with GitHub Copilot CLI? Follow the official GitHub Copilot plugin installation documentation, then add the marketplace and install the plugin:
You can also find the migration skill directly in the repository. Pick one installation method so you don’t end up with duplicate copies.
What the skill checks
Before editing anything, the skill should work out which project, .NET SDK, .NET MAUI packages, Apple workloads, and Xcode version you’re actually using. A, shared package versions, or an existing custom scene delegate can all change the right approach.
The migration covers:
Commit or otherwise save your current work before starting, and go through the resulting diff before accepting it. A successful build doesn’t prove that an authentication redirect or a Home screen shortcut still works.
If you’d rather do things by hand, the instructions are below.
Before you start: check your app and toolchain
Scene lifecycle support was available before Xcode 27, so your app might already use it. If you already have a and matching scene manifest, good news: you can skip the migration steps below. You should still check the toolchain versions and test your app on iOS 27.
At the time of writing, the released .NET 10 iOS and Mac Catalyst workloads 27.0.10722 require Xcode 27.0. Installing a newer Xcode doesn’t automatically make it compatible with that workload. Check the .NET for iOS and Mac Catalyst release notes and use the matching versions rather than disabling the SDK’s Xcode check.
Check your setup
Run for a quick check of your active .NET SDK, .NET MAUI workload, selected Xcode, and Apple SDK compatibility. It also checks supporting components such as the Xcode license, command-line tools, and installed iOS runtimes.
For a .NET 10 app, update your .NET MAUI packages to 10.0.110 or higher before enabling scenes. The two-file launch fix can work with an older .NET MAUI version because already exists there. However, 10.0.110 also includes the scene-side App Actions dispatch and relevant forwarding. Treat the package update as part of the migration, otherwise you might fix the launch and silently break those integrations.
Template projects often get from the installed workload. To pin the minimum version explicitly, add this to your :
If you already manage this version elsewhere, update that value instead. Don’t downgrade a newer compatible version.
Apps created with the .NET MAUI 10.0.110 templates already have the scene configuration. Existing projects aren’t retrofitted by a package or workload update, so you’ll need to do that part yourself.
Add the scene delegate
In your existing app, create and paste in this code. If the file already exists, check its base class and registration instead of adding a second delegate:
Replace with your app’s namespace. For this default setup, keep the class public and the registration name as . If your app already has a custom registered scene delegate, preserve its behavior and use its registered name consistently in the manifest instead of adding a competing delegate.
Yes, that’s the whole class. .NET MAUI does the actual window setup for you. Leave your existing exactly where it is too. The scene delegate works alongside it, not instead of it.
Add the scene manifest
Open as XML and add the following inside the root . If you already have a , update its existing scene configuration instead of adding a duplicate key:
Three details matter here:
You don’t need a. .NET MAUI creates the window in code, and your existing splash screen configuration stays as it is.
If you have custom scene configuration names or override or , review them together rather than replacing the manifest wholesale. The default window-creation path expects ; a different name needs an existing custom implementation that handles window creation.
Some apps select a different plist for Debug, Release, customer, or environment builds. Add or reconcile the manifest in every plist that can produce a shipping app. If you only update the default file, one configuration can still crash right after the splash screen. Preserve the existing or setup while editing these files.
That’s the iOS scene configuration done. If you don’t target Mac Catalyst, skip ahead to the minimum OS versions.
Do the same for Mac Catalyst
If your project also targets Mac Catalyst, make the same two changes there:
Check your minimum OS versions
The .NET for iOS and .NET for Mac Catalyst 27 SDKs also raise the minimum supported versions: iOS needs at least, and Mac Catalyst needs at least (macOS 14 Sonoma). In your app’s , update the existing entries if yours are lower:
These entries belong inside a. Replace the existing values instead of adding duplicates, because a later definition can override your change. Keep each version directly between its XML tags, without leading or trailing whitespace.
Keep a higher deployment target if your app already requires one. If you must retain support for iOS versions below 15 or macOS 12 and 13, moving to these SDKs requires a support-policy decision. Stay on a compatible 26.x SDK while you decide, rather than silently dropping those users. This SDK minimum is separate from scene adoption; see the .NET MAUI template deployment-target guidance.
Check any custom app behavior
With scenes enabled, UIKit no longer calls the activation, backgrounding, URL, and user-activity callbacks. That goes for both registrations and methods you override in , such as or . Move that logic to the corresponding scene events:
Existing event
Scene event
The callback arguments differ, so move the logic instead of just renaming the registration. If you instead override methods in your, call the corresponding implementation. The framework’s window setup, lifecycle events, and authentication forwarding depend on that behavior.
For custom deep links, also handle: URLs and user activities that launch the app arrive in its . That callback runs before .NET MAUI creates the window, so defer navigation until the window is ready.
If your app opens content from a notification tap, inspect as another cold-start input. Preserve that route separately from ordinary push registration and , which remain application-level callbacks.
A regular launch may have no URL or user-activity payload. Guard against absent and before enumerating them, even when the binding’s nullability annotation suggests a value will be there.
still runs, but the standard .NET MAUI scene path creates the window later through the scene delegate. stays . Code that accesses it after needs to use the appropriate scene window when it is ready instead. The cross-platform lifecycle events keep working; do not duplicate that forwarding.
.NET MAUI 10.0.110 handles scene-based App Actions and forwards the relevant URL and user-activity callbacks to. That authentication handling is not general app deep-link navigation and does not replay cold-start links for you. Test authentication and your own links with the app both running and fully closed.
If you use App Actions, make sure every custom handler calls its completion callback exactly once. Pass when the action was handled and when it was not. A handler can respond asynchronously after returning, but returning alone does not acknowledge the action.
Even a handler that only writes a log message needs to respond. As an example, add to , then register this observer inside , before :
The default .NET MAUI builder already forwards App Actions to Essentials. You don’t need this logging observer unless you want it, and you shouldn’t add another registration. If your overrides , that override is no longer called with scenes enabled. Move its custom logic into a registration like the one above, completion callback included.
Rebuild and try it
Restore and build the iOS target:
If the simulator still seems to have an old version of your app, uninstall it, delete the app project’s and folders, and run those two commands again.
Launch both Debug and Release builds on a regular iPhone or iPad simulator running iOS 27 with the matching workload and Xcode. A successful build or launch on iOS 26 doesn’t prove that the app will launch on iOS 27 when built with Xcode 27. Check a cold launch, background and foreground transitions, your authentication flow, links and notification taps with the app both running and closed, and App Actions. If you target Mac Catalyst, build and exercise that target too.
You don’t need an iPhone Duo simulator to verify this migration. Device-specific layouts and the Xcode 27.1 tooling are a separate topic that we’ll cover in a follow-up post soon.
The post Get Your .NET MAUI App Ready for Xcode 27 appeared first on .NET Blog.
That symptom can have many causes. But if the only thing that changed is that you’re now building with Xcode 27 and running the app on iOS 27, and you see the device log message below, your .NET MAUI app needs a few changes:
Code:
[code]UIScene life cycle is required for apps built with this SDK.[/code]UIKit’s scene lifecycle manages each instance of an app’s user interface, including its windows and transitions between foreground and background states. It works alongside the application lifecycle, which manages the app process as a whole. Apple introduced this model with iOS 13, and .NET MAUI already supports it.
This is an Xcode 27 compatibility requirement, not a broken .NET MAUI lifecycle. Existing .NET MAUI projects might still use only the older application lifecycle because updating .NET MAUI or its workloads doesn’t rewrite their platform files. It’s not specific to the iPhone Duo. Every iOS app built with Xcode 27, on any form factor, needs to adopt this pattern. An existing binary built with an older SDK doesn’t suddenly need this migration just because someone updates their phone.
For a straightforward app, the change is small: a scene delegate and an
Code:
Info.plistThat’s where the
Code:
maui-xcode27-migrationLet an AI agent handle the migration
The .NET MAUI CLI can install agent skills into your development environment. A skill is a set of instructions and supporting resources for the agent. It’s not another runtime dependency in your app.
The Xcode 27 migration skill requires Microsoft.Maui.Cli 0.1.0-preview.13.26512.5 or later. Install that preview release:
Code:
[code]dotnet tool install --global Microsoft.Maui.Cli --version 0.1.0-preview.13.26512.5[/code]If you already have it installed, update it instead:
Code:
[code]dotnet tool update --global Microsoft.Maui.Cli --version 0.1.0-preview.13.26512.5[/code]Preview tooling
The .NET MAUI CLI and this skill are experimental and are not covered by the Microsoft Support Policy. The CLI downloads the skill from the public
Code:
dotnet/maui-labsFrom your app’s repository, check that the CLI can find the skill and install it for GitHub Copilot CLI:
Code:
[code]maui ai list skill
maui ai add skill maui-xcode27-migration --env CopilotCli --yes[/code]If you use another AI provider, check
Code:
maui ai add skill --helpCode:
--envRestart or reload your agent session so it picks up the new skill, then ask:
Code:
[code]Prepare this .NET MAUI app for Xcode 27. Inspect the project
and toolchain first, preserve custom lifecycle behavior, and validate
the migration. Ask before changing the SDK or dropping support for
older OS versions.[/code]You don’t need to name the skill in your request. A compatible agent should pick
Code:
maui-xcode27-migrationThe CLI installs the skill, and your coding agent does the migration. There’s no
Code:
maui migratePrefer to manage the skill directly with GitHub Copilot CLI? Follow the official GitHub Copilot plugin installation documentation, then add the
Code:
maui-labsCode:
dotnet-mauiCode:
[code]copilot plugin marketplace add dotnet/maui-labs
copilot plugin install dotnet-maui@dotnet-maui-labs[/code]You can also find the migration skill directly in the
Code:
dotnet/maui-labsWhat the skill checks
Before editing anything, the skill should work out which project, .NET SDK, .NET MAUI packages, Apple workloads, and Xcode version you’re actually using. A
Code:
global.jsonThe migration covers:
- Compatible versions. Match the Apple workload to Xcode, update the required .NET MAUI packages, and check deployment targets without silently dropping older OS support.
- Scene configuration. Add or reconcile the delegate and manifest for the iOS and Mac Catalyst targets, including configuration-specific plist files. Preserve existing scene customizations, launch-screen settings, and multi-window behavior.
- Custom behavior. Review app-delegate overrides as well as lifecycle registrations, authentication, warm and cold links, notification taps, startup work that needs a window, and App Actions.
- Verification. Run a read-only lifecycle audit, review the diff, build the affected targets, and test launch and the behaviors the migration can change. Report anything that could not be exercised.
Commit or otherwise save your current work before starting, and go through the resulting diff before accepting it. A successful build doesn’t prove that an authentication redirect or a Home screen shortcut still works.
If you’d rather do things by hand, the instructions are below.
Before you start: check your app and toolchain
Scene lifecycle support was available before Xcode 27, so your app might already use it. If you already have a
Code:
MauiUISceneDelegateAt the time of writing, the released .NET 10 iOS and Mac Catalyst workloads 27.0.10722 require Xcode 27.0. Installing a newer Xcode doesn’t automatically make it compatible with that workload. Check the .NET for iOS and Mac Catalyst release notes and use the matching versions rather than disabling the SDK’s Xcode check.
Check your setup
Run
Code:
maui doctorFor a .NET 10 app, update your .NET MAUI packages to 10.0.110 or higher before enabling scenes. The two-file launch fix can work with an older .NET MAUI version because
Code:
MauiUISceneDelegateCode:
WebAuthenticatorTemplate projects often get
Code:
$(MauiVersion)Code:
.csprojCode:
[code]<PropertyGroup>
<MauiVersion>10.0.110</MauiVersion>
</PropertyGroup>[/code]If you already manage this version elsewhere, update that value instead. Don’t downgrade a newer compatible version.
Apps created with the .NET MAUI 10.0.110 templates already have the scene configuration. Existing projects aren’t retrofitted by a package or workload update, so you’ll need to do that part yourself.
Add the scene delegate
In your existing app, create
Code:
Platforms/iOS/SceneDelegate.csCode:
[code]using Foundation;
using Microsoft.Maui;
namespace MyMauiApp;
[Register("SceneDelegate")]
public class SceneDelegate : MauiUISceneDelegate
{
}[/code]Replace
Code:
MyMauiAppCode:
SceneDelegateYes, that’s the whole class. .NET MAUI does the actual window setup for you. Leave your existing
Code:
AppDelegate : MauiUIApplicationDelegateAdd the scene manifest
Open
Code:
Platforms/iOS/Info.plistCode:
<dict>Code:
UIApplicationSceneManifestCode:
[code]<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>[/code]Three details matter here:
- Keep exactly as shown. That’s the name .NET MAUI expects.Code:
__MAUI_DEFAULT_SCENE_CONFIGURATION__
- Match the delegate name in the plist with the C# registration: andCode:
SceneDelegatein this example.Code:[Register("SceneDelegate")]
- Leave set toCode:
UIApplicationSupportsMultipleScenesunless your app really supports multiple windows.Code:false
You don’t need a
Code:
UISceneStoryboardFileIf you have custom scene configuration names or override
Code:
GetConfigurationCode:
WillConnectCode:
MauiUISceneDelegateCode:
__MAUI_DEFAULT_SCENE_CONFIGURATION__Some apps select a different plist for Debug, Release, customer, or environment builds. Add or reconcile the manifest in every plist that can produce a shipping app. If you only update the default file, one configuration can still crash right after the splash screen. Preserve the existing
Code:
MauiSplashScreenCode:
UILaunchStoryboardNameThat’s the iOS scene configuration done. If you don’t target Mac Catalyst, skip ahead to the minimum OS versions.
Do the same for Mac Catalyst
If your project also targets Mac Catalyst, make the same two changes there:
- Add .Code:
Platforms/MacCatalyst/SceneDelegate.cs
- Add the same section toCode:
UIApplicationSceneManifest.Code:Platforms/MacCatalyst/Info.plist
Check your minimum OS versions
The .NET for iOS and .NET for Mac Catalyst 27 SDKs also raise the minimum supported versions: iOS needs at least
Code:
15.0Code:
17.0Code:
.csprojCode:
SupportedOSPlatformVersionCode:
[code]<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">17.0</SupportedOSPlatformVersion>[/code]These entries belong inside a
Code:
<PropertyGroup>Keep a higher deployment target if your app already requires one. If you must retain support for iOS versions below 15 or macOS 12 and 13, moving to these SDKs requires a support-policy decision. Stay on a compatible 26.x SDK while you decide, rather than silently dropping those users. This SDK minimum is separate from scene adoption; see the .NET MAUI template deployment-target guidance.
Check any custom app behavior
With scenes enabled, UIKit no longer calls the
Code:
AppDelegateCode:
ConfigureLifecycleEventsCode:
AppDelegateCode:
OpenUrlCode:
ContinueUserActivityExisting event
Scene event
Code:
OnActivatedCode:
SceneOnActivatedCode:
OnResignActivationCode:
SceneOnResignActivationCode:
DidEnterBackgroundCode:
SceneDidEnterBackgroundCode:
WillEnterForegroundCode:
SceneWillEnterForegroundCode:
OpenUrlCode:
SceneOpenUrlCode:
ContinueUserActivityCode:
SceneContinueUserActivityThe callback arguments differ, so move the logic instead of just renaming the registration. If you instead override methods in your
Code:
SceneDelegateCode:
baseFor custom deep links, also handle
Code:
SceneWillConnectCode:
UISceneConnectionOptionsIf your app opens content from a notification tap, inspect
Code:
connectionOptions.NotificationResponseCode:
DidReceiveRemoteNotificationA regular launch may have no URL or user-activity payload. Guard against absent
Code:
UrlContextsCode:
UserActivitiesCode:
FinishedLaunchingCode:
AppDelegate.WindowCode:
nullCode:
base.FinishedLaunchingCode:
Window.NET MAUI 10.0.110 handles scene-based App Actions and forwards the relevant URL and user-activity callbacks to
Code:
WebAuthenticatorIf you use App Actions, make sure every custom
Code:
PerformActionForShortcutItemCode:
trueCode:
falseEven a handler that only writes a log message needs to respond. As an example, add
Code:
using Microsoft.Maui.LifecycleEvents;Code:
MauiProgram.csCode:
CreateMauiAppCode:
return builder.Build();Code:
[code]builder.ConfigureLifecycleEvents(events =>
{
#if IOS || MACCATALYST
events.AddiOS(ios => ios
.PerformActionForShortcutItem((application, shortcutItem, completion) =>
{
Console.WriteLine($"App Action received: {shortcutItem.Type}");
completion(false);
}));
#endif
});[/code]The default .NET MAUI builder already forwards App Actions to Essentials. You don’t need this logging observer unless you want it, and you shouldn’t add another
Code:
Platform.PerformActionForShortcutItemCode:
AppDelegateCode:
PerformActionForShortcutItemRebuild and try it
Restore and build the iOS target:
Code:
[code]dotnet restore -p:TargetFramework=net10.0-ios
dotnet build -f net10.0-ios --no-restore[/code]If the simulator still seems to have an old version of your app, uninstall it, delete the app project’s
Code:
binCode:
objLaunch both Debug and Release builds on a regular iPhone or iPad simulator running iOS 27 with the matching workload and Xcode. A successful build or launch on iOS 26 doesn’t prove that the app will launch on iOS 27 when built with Xcode 27. Check a cold launch, background and foreground transitions, your authentication flow, links and notification taps with the app both running and closed, and App Actions. If you target Mac Catalyst, build and exercise that target too.
You don’t need an iPhone Duo simulator to verify this migration. Device-specific layouts and the Xcode 27.1 tooling are a separate topic that we’ll cover in a follow-up post soon.
The post Get Your .NET MAUI App Ready for Xcode 27 appeared first on .NET Blog.

