Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[DevBlog MS] Get Your .NET MAUI App Ready for Xcode 27
1
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:

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.plist
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
Code:
maui-xcode27-migration
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:

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-labs
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:

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 --help
and change the
Code:
--env
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:

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-migration
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
Code:
maui migrate
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
Code:
maui-labs
marketplace and install the
Code:
dotnet-maui
plugin:

Code:
[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-labs
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
Code:
global.json
, shared package versions, or an existing custom scene delegate can all change the right approach.

The 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:
MauiUISceneDelegate
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
Code:
maui doctor
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
Code:
MauiUISceneDelegate
already exists there. However, 10.0.110 also includes the scene-side App Actions dispatch and relevant
Code:
WebAuthenticator
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
Code:
$(MauiVersion)
from the installed workload. To pin the minimum version explicitly, add this to your
Code:
.csproj
:

Code:
[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.cs
and paste in this code. If the file already exists, check its base class and registration instead of adding a second delegate:

Code:
[code]using Foundation; using Microsoft.Maui; namespace MyMauiApp; [Register("SceneDelegate")] public class SceneDelegate : MauiUISceneDelegate { }[/code]

Replace
Code:
MyMauiApp
with your app’s namespace. For this default setup, keep the class public and the registration name as
Code:
SceneDelegate
. 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
Code:
AppDelegate : MauiUIApplicationDelegate
exactly where it is too. The scene delegate works alongside it, not instead of it.

Add the scene manifest

Open
Code:
Platforms/iOS/Info.plist
as XML and add the following inside the root
Code:
<dict>
. If you already have a
Code:
UIApplicationSceneManifest
, update its existing scene configuration instead of adding a duplicate key:

Code:
[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
    Code:
    __MAUI_DEFAULT_SCENE_CONFIGURATION__
    exactly as shown. That’s the name .NET MAUI expects.

  • Match the delegate name in the plist with the C# registration:
    Code:
    SceneDelegate
    and
    Code:
    [Register("SceneDelegate")]
    in this example.

  • Leave
    Code:
    UIApplicationSupportsMultipleScenes
    set to
    Code:
    false
    unless your app really supports multiple windows.


You don’t need a
Code:
UISceneStoryboardFile
. .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
Code:
GetConfiguration
or
Code:
WillConnect
, review them together rather than replacing the manifest wholesale. The default
Code:
MauiUISceneDelegate
window-creation path expects
Code:
__MAUI_DEFAULT_SCENE_CONFIGURATION__
; 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
Code:
MauiSplashScreen
or
Code:
UILaunchStoryboardName
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:

  1. Add
    Code:
    Platforms/MacCatalyst/SceneDelegate.cs
    .

  2. Add the same
    Code:
    UIApplicationSceneManifest
    section to
    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.0
, and Mac Catalyst needs at least
Code:
17.0
(macOS 14 Sonoma). In your app’s
Code:
.csproj
, update the existing
Code:
SupportedOSPlatformVersion
entries if yours are lower:

Code:
[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>
. 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
Code:
AppDelegate
activation, backgrounding, URL, and user-activity callbacks. That goes for both
Code:
ConfigureLifecycleEvents
registrations and methods you override in
Code:
AppDelegate
, such as
Code:
OpenUrl
or
Code:
ContinueUserActivity
. Move that logic to the corresponding scene events:

Existing event
Scene event

Code:
OnActivated

Code:
SceneOnActivated

Code:
OnResignActivation

Code:
SceneOnResignActivation

Code:
DidEnterBackground

Code:
SceneDidEnterBackground

Code:
WillEnterForeground

Code:
SceneWillEnterForeground

Code:
OpenUrl

Code:
SceneOpenUrl

Code:
ContinueUserActivity

Code:
SceneContinueUserActivity

The callback arguments differ, so move the logic instead of just renaming the registration. If you instead override methods in your
Code:
SceneDelegate
, call the corresponding
Code:
base
implementation. The framework’s window setup, lifecycle events, and authentication forwarding depend on that behavior.

For custom deep links, also handle
Code:
SceneWillConnect
: URLs and user activities that launch the app arrive in its
Code:
UISceneConnectionOptions
. 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
Code:
connectionOptions.NotificationResponse
as another cold-start input. Preserve that route separately from ordinary push registration and
Code:
DidReceiveRemoteNotification
, which remain application-level callbacks.

A regular launch may have no URL or user-activity payload. Guard against absent
Code:
UrlContexts
and
Code:
UserActivities
before enumerating them, even when the binding’s nullability annotation suggests a value will be there.

Code:
FinishedLaunching
still runs, but the standard .NET MAUI scene path creates the window later through the scene delegate.
Code:
AppDelegate.Window
stays
Code:
null
. Code that accesses it after
Code:
base.FinishedLaunching
needs to use the appropriate scene window when it is ready instead. The cross-platform
Code:
Window
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
Code:
WebAuthenticator
. 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
Code:
PerformActionForShortcutItem
handler calls its completion callback exactly once. Pass
Code:
true
when the action was handled and
Code:
false
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
Code:
using Microsoft.Maui.LifecycleEvents;
to
Code:
MauiProgram.cs
, then register this observer inside
Code:
CreateMauiApp
, before
Code:
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.PerformActionForShortcutItem
registration. If your
Code:
AppDelegate
overrides
Code:
PerformActionForShortcutItem
, 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:

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:
bin
and
Code:
obj
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.
Reply

Users browsing this thread:
1 Guest(s)


Forum Jump: