App startup, activation and platform state
Part of Migrating to Uno Platform 7.0.
Read this if you customize an Android Application class, override Application.OnActivated, subscribe to Application.Suspending and friends, handle activations on iOS or tvOS, read ApplicationData settings on iOS or tvOS, or use ContextHelper on Android.
Skip it if your app targets none of these and uses the unmodified template App class.
Android head uses the host builder
The Android head now builds its host through UnoPlatformHostBuilder, like every other target.
NativeApplication (now Uno.UI.Runtime.Android.NativeApplication, see Runtime host renames) is now abstract; its AppBuilder delegate type and the
constructor taking one have been removed, replaced by an abstract CreateHost() method.
// Before
public class Application : Microsoft.UI.Xaml.NativeApplication
{
public Application(IntPtr javaReference, JniHandleOwnership transfer)
: base(() => new App(), javaReference, transfer)
{
}
}
// After
using Uno.UI.Hosting;
public class Application : Uno.UI.Runtime.Android.NativeApplication
{
public Application(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected override UnoPlatformHost CreateHost() =>
UnoPlatformHostBuilder.Create()
.App(() => new App())
.UseAndroid()
.Build();
}
Two related behavior changes:
- Startup failures now propagate. Previously an exception during Android host initialization was written to the console and swallowed, leaving a blank screen. It is now logged and rethrown. Apps with latent initialization failures that used to start degraded will now crash on launch.
Uno.UI.Runtime.Skia.Android.AndroidHostis nowinternal. UseUseAndroid()instead of constructing it.
See Customizing the Application class on Android.
See also: Runtime host renames, Vulkan on Android.
Activation is read from AppInstance
Application.OnActivated is removed. It is not part of the shipping WinUI 3 surface —
microsoft.ui.xaml.coretypes2.idl declares it only behind the UWP-support feature gate — and a Windows
App SDK app discovers how it was activated through Microsoft.Windows.AppLifecycle.AppInstance instead.
An existing override now fails to compile, which is deliberate: a method that silently stopped being
invoked would be far harder to notice than a build error.
OnLaunched always runs, and always receives plain ActivationKind.Launch arguments, matching WinUI's
FrameworkApplication, which synthesizes them regardless of how the process was activated. The real
payload comes from AppInstance:
// Before
protected override void OnActivated(IActivatedEventArgs args)
{
if (args.Kind == ActivationKind.Protocol)
{
var uri = ((ProtocolActivatedEventArgs)args).Uri;
// …
}
}
// After
using Microsoft.Windows.AppLifecycle;
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
// …create and activate the window as usual…
var activation = AppInstance.GetCurrent().GetActivatedEventArgs();
if (activation.Kind == ExtendedActivationKind.Protocol &&
activation.Data is Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs protocol)
{
var uri = protocol.Uri;
// …
}
}
GetActivatedEventArgs() never returns null and never throws; with no activation payload it reports
ExtendedActivationKind.Launch over the process command line. It always describes the activation the
process started with, so an activation that arrives while the app is already running is raised on
AppInstance.GetCurrent().Activated — subscribe to it in the App constructor. See
Custom protocol activation for a complete App.xaml.cs.
On iOS and tvOS under the UIKit scene lifecycle, a cold-start
activation arrives after OnLaunched. UIKit only hands the app the URL, universal link or home-screen
shortcut it was started with when the first scene connects, which happens after OnLaunched has run. At
that point GetActivatedEventArgs() still reports Launch; the real activation is then raised on
AppInstance.GetCurrent().Activated, and GetActivatedEventArgs() returns it from then on. Handle the
Activated event as well as reading GetActivatedEventArgs() in OnLaunched, and the app gets the
activation on every target.
Application.OnLaunched is protected virtual, not Uno's protected internal virtual, matching
WinUI. Declare the override as protected override void OnLaunched(LaunchActivatedEventArgs args) —
which is what the templates generate, and what C# already required of an override in another assembly.
On WebAssembly, LaunchActivatedEventArgs.Arguments no longer carries the internal
unoprotocolactivation query key that transports a protocol activation across the browser
navigation. The key is stripped before the app sees the arguments, so code that parsed it out of
Arguments should read the URI from GetActivatedEventArgs() instead.
App lifecycle events move to CoreApplication
Application.Suspending, Application.Resuming, Application.EnteredBackground and
Application.LeavingBackground are removed. Subscribe to the events of the same names on
Windows.ApplicationModel.Core.CoreApplication, which Uno Platform already raised in lockstep with
these — including the SuspendingOperation deferral, so handler bodies need no change:
// Before
this.Suspending += OnSuspending;
this.Resuming += OnResuming;
// After
using Windows.ApplicationModel.Core;
CoreApplication.Suspending += OnSuspending;
CoreApplication.Resuming += OnResuming;
The handler signature changes from the SuspendingEventHandler family to
EventHandler<SuspendingEventArgs>, so a nullable-enabled file needs object? sender. The
SuspendingEventHandler, EnteredBackgroundEventHandler and LeavingBackgroundEventHandler
delegates themselves remain — they are still Windows App SDK types.
Like OnActivated, all four sat behind the UWP-support feature gate in WinUI: the shipping WinUI 3
Application exposes only ResourceManagerRequested and UnhandledException. CoreApplication is
also the more portable choice, being Windows Runtime API that compiles against the Windows App SDK,
which Application.Suspending does not.
On Skia Desktop, suspension is no longer reported at all. Windows App SDK desktop apps have no
suspended state, and Uno Platform previously reported one on Windows only, when a window closed —
which also skipped the EnteredBackground that precedes Suspending. Save state from
Window.Closed instead. Android, iOS and tvOS report the real platform lifecycle as before.
Application.RequiresPointerMode is now internal. It is not part of the public Windows App SDK
Application surface either, and nothing in Uno Platform's rendering depends on the value an app sets.
Delete the assignment; there is no replacement to move to.
See also: Window events.
UIKit scene lifecycle on iOS and tvOS
Apps built with Xcode 27 and its version 27 SDKs must use the UIKit scene lifecycle, whatever
their .NET version, or iOS and tvOS 27 refuse to launch them. An existing app needs no scene manifest
for this: when its Info.plist has none, Uno Platform adopts the scene lifecycle on its behalf
once the app is built with a version 27 SDK, and hosts the app's window in its scene. The switch
therefore comes with the move to Xcode 27, not with the upgrade to 7.0: an app built with an
older SDK keeps the app lifecycle unless it sets UnoUseSceneLifecycle. An app that declares a
manifest keeps using it.
Under the scene lifecycle UIKit no longer calls the application delegate's URL, user activity,
home-screen shortcut, and foreground and background methods. For an app without a manifest, Uno
Platform forwards them to the application delegate, so a custom UnoUIApplicationDelegate keeps
receiving them. The activation the app was launched with then arrives that way after OnLaunched,
rather than in FinishedLaunching's launch options. See
Scene lifecycle, and for how
the app reads that activation, Activation is read from AppInstance.
See also: Minimum OS versions raised.
Application settings on iOS and tvOS
Values stored through ApplicationData.Current.LocalSettings / .RoamingSettings used to be
written directly into the shared NSUserDefaults.StandardUserDefaults domain. In 7.0 they
are stored in a dedicated NSUserDefaults suite named UnoApplicationData, persisted as
Library/Preferences/UnoApplicationData.plist inside the app sandbox.
This isolates Uno-managed settings from the keys the OS, Apple frameworks, and native
libraries keep in the standard domain: enumerating (Values.Keys, Values.Count) or
clearing (Values.Clear()) application settings no longer sees — or deletes — unrelated
native keys.
Nothing is migrated for you. A new app is unaffected. An app updating from an earlier
Uno Platform version keeps its old values in the standard defaults, where the
ApplicationData API no longer looks — until you ask for them:
#if __IOS__ || __TVOS__
// Call once during startup, before the settings are first read.
var migrated = Uno.Storage.ApplicationDataMigrator.MigrateSettings();
#endif
MigrateSettings() moves the entries an earlier Uno Platform version wrote (recognized by
Uno's serialized TypeName:value format) out of the standard defaults and into the
UnoApplicationData suite, and returns how many it took. It is safe to call on every
launch: a key that already exists in the new suite keeps its current value, and an install
with nothing to migrate is a no-op. If the new suite cannot be saved, it throws an
IOException and leaves the old values where they are, so a later launch can retry.
Also update native/interop code that reads these values directly from the standard defaults:
// In 7.0 and later, read the values from the dedicated UnoApplicationData suite
// (before 7.0 they were in NSUserDefaults.StandardUserDefaults)
var unoDefaults = new NSUserDefaults("UnoApplicationData", NSUserDefaultsType.SuiteName);
// Swift companion code
let unoDefaults = UserDefaults(suiteName: "UnoApplicationData")
Values your app writes to the standard defaults itself through native APIs are not
affected — they stay where they are and remain invisible to ApplicationData, as before.
Important
The migration is one-way. Once you call MigrateSettings(), the moved values are gone from
NSUserDefaults.StandardUserDefaults, so downgrading to a pre-7.0 build of your app will
not find them there anymore.
See Application Data and Settings for details on where each platform stores its data.
Android context APIs
Skia-on-Android no longer keeps the window, its render stack, or its input sources in process-wide statics, so the ambient Android context APIs changed shape:
Uno.UI.ContextHelper.Currentis now typedAndroid.Content.Context?. It has always been able to benull— before any activity is created — but the property was annotated non-null, so the compiler never surfaced it. Code that dereferences it directly will now warn under nullable reference types. Guard it, or use the newContextHelper.ApplicationContextwhen a process-wide context is all that is needed (system services, resources, package info).Uno.UI.ContextHelper.Currentnow tracks the most recently active live activity. It stays on an activity while it is paused or stopped, so background work still resolves one, and moves to another live activity (ornull) when that activity is destroyed. It previously kept the last activity ever assigned, including a destroyed one. It is not necessarily the foreground activity, so avoid it for work that needs a resumed activity.- The
Uno.UI.ContextHelper.Currentsetter is nowinternal. The value is owned by the activity lifecycle (BaseActivity), which overwrote any value app code assigned. Uno.UI.ViewManagement.ApplicationViewHelper.GetBaseActivityEvents()andIBaseActivityEventswere removed. No Skia activity implements the interface, so the method always returnednull. Observe activity lifecycle events with Android'sApplication.RegisterActivityLifecycleCallbacksinstead. TheUno.UI.ViewManagement.Activity*Handlerdelegates it used were removed with it.Uno.UI.OnSystemUiVisibilityChangeListeneris nowinternal. It is constructed by the host with the activity that owns the window; app code had no way to supply one.
Related pages
- Namespace, assembly and host renames for the renamed host types.
- Project format, target frameworks and OS versions for minimum OS versions.
- Behavior changes to re-test for what to re-test on devices.
- Errors, symptoms and code search to go from an error or symptom to its fix.
- Upgrade steps to return to the main path.