diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 4d608a6..87a85f4 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -1,19 +1,49 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
- "$ref": "#/definitions/build",
- "title": "Build Schema",
"definitions": {
- "build": {
- "type": "object",
+ "Host": {
+ "type": "string",
+ "enum": [
+ "AppVeyor",
+ "AzurePipelines",
+ "Bamboo",
+ "Bitbucket",
+ "Bitrise",
+ "GitHubActions",
+ "GitLab",
+ "Jenkins",
+ "Rider",
+ "SpaceAutomation",
+ "TeamCity",
+ "Terminal",
+ "TravisCI",
+ "VisualStudio",
+ "VSCode"
+ ]
+ },
+ "ExecutableTarget": {
+ "type": "string",
+ "enum": [
+ "Clean",
+ "Compile",
+ "Deploy",
+ "Pack",
+ "Print",
+ "Restore"
+ ]
+ },
+ "Verbosity": {
+ "type": "string",
+ "description": "",
+ "enum": [
+ "Verbose",
+ "Normal",
+ "Minimal",
+ "Quiet"
+ ]
+ },
+ "NukeBuild": {
"properties": {
- "Configuration": {
- "type": "string",
- "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
- "enum": [
- "Debug",
- "Release"
- ]
- },
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
@@ -23,34 +53,13 @@
"description": "Shows the help text for this build assembly"
},
"Host": {
- "type": "string",
"description": "Host for execution. Default is 'automatic'",
- "enum": [
- "AppVeyor",
- "AzurePipelines",
- "Bamboo",
- "Bitbucket",
- "Bitrise",
- "GitHubActions",
- "GitLab",
- "Jenkins",
- "Rider",
- "SpaceAutomation",
- "TeamCity",
- "Terminal",
- "TravisCI",
- "VisualStudio",
- "VSCode"
- ]
+ "$ref": "#/definitions/Host"
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
- "NuGetApiKey": {
- "type": "string",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
"Partition": {
"type": "string",
"description": "Partition to use on CI"
@@ -74,47 +83,46 @@
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
- "type": "string",
- "enum": [
- "Clean",
- "Compile",
- "Deploy",
- "Pack",
- "Print",
- "Restore"
- ]
+ "$ref": "#/definitions/ExecutableTarget"
}
},
- "Solution": {
- "type": "string",
- "description": "Path to a solution file that is automatically loaded"
- },
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
- "type": "string",
- "enum": [
- "Clean",
- "Compile",
- "Deploy",
- "Pack",
- "Print",
- "Restore"
- ]
+ "$ref": "#/definitions/ExecutableTarget"
}
},
"Verbosity": {
- "type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
+ "$ref": "#/definitions/Verbosity"
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "Configuration": {
+ "type": "string",
+ "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
"enum": [
- "Minimal",
- "Normal",
- "Quiet",
- "Verbose"
+ "Debug",
+ "Release"
]
+ },
+ "NuGetApiKey": {
+ "type": "string",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
+ },
+ "Solution": {
+ "type": "string",
+ "description": "Path to a solution file that is automatically loaded"
}
}
+ },
+ {
+ "$ref": "#/definitions/NukeBuild"
}
- }
+ ]
}
diff --git a/build/Build.cs b/build/Build.cs
index 215f8e2..0ddae52 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -60,7 +60,11 @@ partial class Build : NukeBuild
Target Restore => _ => _
.DependsOn(Clean)
- .Executes(() => DotNetRestore(s => s.SetProjectFile(Solution)));
+ .Executes(() =>
+ {
+ DotNetWorkloadRestore(s => s.DisableSkipManifestUpdate().SetProject(Solution));
+ return DotNetRestore(s => s.SetProjectFile(Solution));
+ });
Target Compile => _ => _
.DependsOn(Restore, Print)
diff --git a/src/Extensions.Hosting.MainUIThread/IUiContext.cs b/src/Extensions.Hosting.MainUIThread/IUiContext.cs
index 579420d..65a4058 100644
--- a/src/Extensions.Hosting.MainUIThread/IUiContext.cs
+++ b/src/Extensions.Hosting.MainUIThread/IUiContext.cs
@@ -15,7 +15,7 @@ public interface IUiContext
bool IsLifetimeLinked { get; set; }
///
- /// Gets or sets a value indicating whether is the WPF application started and still running?.
+ /// Gets or sets a value indicating whether is the application started and still running?.
///
bool IsRunning { get; set; }
}
diff --git a/src/Extensions.Hosting.Maui.Example/App.xaml b/src/Extensions.Hosting.Maui.Example/App.xaml
new file mode 100644
index 0000000..578fd33
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/App.xaml.cs b/src/Extensions.Hosting.Maui.Example/App.xaml.cs
new file mode 100644
index 0000000..7d4a25a
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/App.xaml.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// App.
+///
+///
+public partial class App : Application
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public App()
+ {
+ InitializeComponent();
+
+ // Start the host
+ MauiProgram.HostApp.StartAsync();
+ }
+
+ ///
+ /// Creates the window.
+ ///
+ /// State of the activation.
+ /// A Window.
+ protected override Window CreateWindow(IActivationState? activationState) => new(new AppShell());
+}
diff --git a/src/Extensions.Hosting.Maui.Example/AppShell.xaml b/src/Extensions.Hosting.Maui.Example/AppShell.xaml
new file mode 100644
index 0000000..4f6a19d
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/AppShell.xaml.cs b/src/Extensions.Hosting.Maui.Example/AppShell.xaml.cs
new file mode 100644
index 0000000..67ac278
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/AppShell.xaml.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// AppShell.
+///
+///
+public partial class AppShell : Shell
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AppShell() => InitializeComponent();
+}
diff --git a/src/Extensions.Hosting.Maui.Example/ExampleMauiService.cs b/src/Extensions.Hosting.Maui.Example/ExampleMauiService.cs
new file mode 100644
index 0000000..9abf93d
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/ExampleMauiService.cs
@@ -0,0 +1,23 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using ReactiveMarbles.Extensions.Hosting.Maui;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// Example MAUI service.
+///
+public class ExampleMauiService : IMauiService
+{
+ ///
+ /// Initializes the specified application.
+ ///
+ /// The application.
+ public void Initialize(Microsoft.Maui.Controls.Application application)
+ {
+ // Example initialization
+ Console.WriteLine("MAUI application initialized via IMauiService");
+ }
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Extensions.Hosting.Maui.Example.csproj b/src/Extensions.Hosting.Maui.Example/Extensions.Hosting.Maui.Example.csproj
new file mode 100644
index 0000000..bbcab96
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Extensions.Hosting.Maui.Example.csproj
@@ -0,0 +1,78 @@
+
+
+
+ false
+ net9.0-android;
+ $(TargetFrameworks);net9.0-windows10.0.19041.0
+
+
+
+
+ Exe
+ Extensions.Hosting.Maui.Example
+ true
+ true
+ enable
+ enable
+
+
+ Extensions.Hosting.Maui.Example
+
+
+ com.companyname.extensions.hosting.maui.example
+
+
+ 1.0
+ 1
+
+
+ None
+
+ 15.0
+ 15.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ NU1605;SA1633;SA1600;SA1601;SA1027;SA1137;SX1309;CA1805;CA1400;RCS1102;SA1400;SA1508;SA1512;SA1642;SX1101
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/MainPage.xaml b/src/Extensions.Hosting.Maui.Example/MainPage.xaml
new file mode 100644
index 0000000..0a81bb6
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/MainPage.xaml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/MainPage.xaml.cs b/src/Extensions.Hosting.Maui.Example/MainPage.xaml.cs
new file mode 100644
index 0000000..7c82ec3
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/MainPage.xaml.cs
@@ -0,0 +1,38 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using ReactiveMarbles.Extensions.Hosting.Maui;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// MainPage.
+///
+public partial class MainPage : ContentPage, IMauiShell
+{
+ private int _count = 0;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainPage() => InitializeComponent();
+
+ private void OnCounterClicked(object? sender, EventArgs e)
+ {
+ _count++;
+
+ if (_count == 1)
+ {
+ CounterBtn.Text = $"Clicked {_count} time";
+ }
+ else
+ {
+ CounterBtn.Text = $"Clicked {_count} times";
+ }
+
+ SemanticScreenReader.Announce(CounterBtn.Text);
+ }
+
+ private async void OnNavigateClicked(object? sender, EventArgs e) => await Navigation.PushAsync(new SecondPage());
+}
diff --git a/src/Extensions.Hosting.Maui.Example/MauiProgram.cs b/src/Extensions.Hosting.Maui.Example/MauiProgram.cs
new file mode 100644
index 0000000..6119228
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/MauiProgram.cs
@@ -0,0 +1,66 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using ReactiveMarbles.Extensions.Hosting.Maui;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// MauiProgram.
+///
+public static class MauiProgram
+{
+ ///
+ /// Initializes the class.
+ ///
+ static MauiProgram()
+ {
+ // Create the host
+ var builder = Host.CreateApplicationBuilder();
+ builder.Services.AddSingleton();
+ builder
+ .ConfigureMaui(maui =>
+ {
+ maui.UseMauiApp(mauiapp =>
+ {
+ mauiapp
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+#if DEBUG
+ mauiapp.Logging.AddDebug();
+#endif
+ });
+ maui.AddSingletonPage();
+ maui.AddSingletonPage();
+ maui.ConfigureContext(ctx =>
+ {
+ // Example: configure the MAUI context
+ ctx.IsLifetimeLinked = true;
+ });
+ })
+ .UseMauiLifetime();
+
+ HostApp = builder.Build();
+ }
+
+ ///
+ /// Gets the host.
+ ///
+ ///
+ /// The host.
+ ///
+ public static IHost HostApp { get; }
+
+ ///
+ /// Creates the maui app.
+ ///
+ /// A MauiApp.
+ public static MauiApp GetMauiApp() => HostApp.Services.GetRequiredService();
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Android/AndroidManifest.xml b/src/Extensions.Hosting.Maui.Example/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..e9937ad
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Android/MainActivity.cs b/src/Extensions.Hosting.Maui.Example/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..5a82a4a
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Android/MainActivity.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Android.App;
+using Android.Content.PM;
+
+namespace Extensions.Hosting.Maui.Example;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Android/MainApplication.cs b/src/Extensions.Hosting.Maui.Example/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..aa213e9
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Android/MainApplication.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Android.App;
+using Android.Runtime;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// MainApplication.
+///
+///
+[Application]
+public class MainApplication : MauiApplication
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The handle.
+ /// The ownership.
+ /// The service provider.
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership, IServiceProvider serviceProvider)
+ : base(handle, ownership) => _serviceProvider = serviceProvider;
+
+ protected override MauiApp CreateMauiApp() => _serviceProvider.GetRequiredService();
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Android/Resources/values/colors.xml b/src/Extensions.Hosting.Maui.Example/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..c04d749
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/AppDelegate.cs b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 0000000..1657142
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,16 @@
+using Foundation;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// AppDelegate.
+///
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ ///
+ /// Creates the maui application.
+ ///
+ /// A MauiApp.
+ protected override MauiApp CreateMauiApp() => MauiProgram.GetMauiApp();
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Entitlements.plist b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Entitlements.plist
new file mode 100644
index 0000000..de4adc9
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Entitlements.plist
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+ com.apple.security.network.client
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Info.plist b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..f2e0987
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UIDeviceFamily
+
+ 2
+
+ LSApplicationCategoryType
+ public.app-category.lifestyle
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Program.cs b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 0000000..bfdbfa1
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,21 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// Program.
+///
+public class Program
+{
+ ///
+ /// Defines the entry point of the application.
+ ///
+ /// The arguments.
+ public static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Windows/App.xaml b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..e2e853a
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Windows/App.xaml.cs b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..c23cfc4
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,24 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace Extensions.Hosting.Maui.Example.WinUI;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App() => InitializeComponent();
+
+ ///
+ /// Creates the maui application.
+ ///
+ /// A MauiApp.
+ protected override MauiApp CreateMauiApp() => MauiProgram.GetMauiApp();
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Windows/Package.appxmanifest b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..f0b84be
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/Windows/app.manifest b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..1dba9c6
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/Windows/app.manifest
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+ true
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/iOS/AppDelegate.cs b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..1657142
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,16 @@
+using Foundation;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// AppDelegate.
+///
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ ///
+ /// Creates the maui application.
+ ///
+ /// A MauiApp.
+ protected override MauiApp CreateMauiApp() => MauiProgram.GetMauiApp();
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Info.plist b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..0004a4f
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Program.cs b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..bfdbfa1
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Program.cs
@@ -0,0 +1,21 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// Program.
+///
+public class Program
+{
+ ///
+ /// Defines the entry point of the application.
+ ///
+ /// The arguments.
+ public static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Resources/PrivacyInfo.xcprivacy b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000..24ab3b4
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
@@ -0,0 +1,51 @@
+
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 35F9.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ E174.1
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/Properties/launchSettings.json b/src/Extensions.Hosting.Maui.Example/Properties/launchSettings.json
new file mode 100644
index 0000000..4f85793
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "Project",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/AppIcon/appicon.svg b/src/Extensions.Hosting.Maui.Example/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..9d63b65
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/AppIcon/appiconfg.svg b/src/Extensions.Hosting.Maui.Example/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Fonts/OpenSans-Regular.ttf b/src/Extensions.Hosting.Maui.Example/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..a69d2fb
Binary files /dev/null and b/src/Extensions.Hosting.Maui.Example/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Fonts/OpenSans-Semibold.ttf b/src/Extensions.Hosting.Maui.Example/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..8171ed3
Binary files /dev/null and b/src/Extensions.Hosting.Maui.Example/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Images/dotnet_bot.png b/src/Extensions.Hosting.Maui.Example/Resources/Images/dotnet_bot.png
new file mode 100644
index 0000000..054167e
Binary files /dev/null and b/src/Extensions.Hosting.Maui.Example/Resources/Images/dotnet_bot.png differ
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Raw/AboutAssets.txt b/src/Extensions.Hosting.Maui.Example/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..89dc758
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with your package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Splash/splash.svg b/src/Extensions.Hosting.Maui.Example/Resources/Splash/splash.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Styles/Colors.xaml b/src/Extensions.Hosting.Maui.Example/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..d57fcc6
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+
+ #512BD4
+ #ac99ea
+ #242424
+ #DFD8F7
+ #9880e5
+ #2B0B98
+
+ White
+ Black
+ #D600AA
+ #190649
+ #1f1f1f
+
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Extensions.Hosting.Maui.Example/Resources/Styles/Styles.xaml b/src/Extensions.Hosting.Maui.Example/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..5fef12a
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/Resources/Styles/Styles.xaml
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/SecondPage.xaml b/src/Extensions.Hosting.Maui.Example/SecondPage.xaml
new file mode 100644
index 0000000..a8e5f7e
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/SecondPage.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui.Example/SecondPage.xaml.cs b/src/Extensions.Hosting.Maui.Example/SecondPage.xaml.cs
new file mode 100644
index 0000000..1c74e42
--- /dev/null
+++ b/src/Extensions.Hosting.Maui.Example/SecondPage.xaml.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+namespace Extensions.Hosting.Maui.Example;
+
+///
+/// SecondPage.
+///
+///
+public partial class SecondPage : ContentPage
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SecondPage() => InitializeComponent();
+
+ private async void OnNavigateClicked(object? sender, EventArgs e) => await Navigation.PopAsync();
+}
diff --git a/src/Extensions.Hosting.Maui/Extensions.Hosting.Maui.csproj b/src/Extensions.Hosting.Maui/Extensions.Hosting.Maui.csproj
new file mode 100644
index 0000000..3e30467
--- /dev/null
+++ b/src/Extensions.Hosting.Maui/Extensions.Hosting.Maui.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net8.0;net9.0;net10.0
+ true
+ This extension adds MAUI support to generic host based applications. With this you can enhance your application with a UI, and use all the services provided by the generic host like DI, logging etc.
+ CP.Extensions.Hosting.Maui
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions.Hosting.Maui/HostBuilderMauiExtensions.cs b/src/Extensions.Hosting.Maui/HostBuilderMauiExtensions.cs
new file mode 100644
index 0000000..223a13c
--- /dev/null
+++ b/src/Extensions.Hosting.Maui/HostBuilderMauiExtensions.cs
@@ -0,0 +1,239 @@
+// Copyright (c) 2019-2025 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Maui.Controls;
+using ReactiveMarbles.Extensions.Hosting.Maui.Internals;
+
+namespace ReactiveMarbles.Extensions.Hosting.Maui;
+
+///
+/// This contains the MAUI extensions for Microsoft.Extensions.Hosting.
+///
+public static class HostBuilderMauiExtensions
+{
+ private const string MauiContextKey = nameof(MauiContext);
+
+ ///
+ /// Defines that stopping the MAUI application also stops the host (application).
+ ///
+ /// IHostBuilder.
+ /// A IHostBuilder.
+ public static IHostBuilder? UseMauiLifetime(this IHostBuilder hostBuilder) =>
+ hostBuilder?.ConfigureServices((_, __) =>
+ {
+ TryRetrieveMauiContext(hostBuilder.Properties, out var mauiContext);
+ mauiContext.IsLifetimeLinked = true;
+ });
+
+ ///
+ /// Defines that stopping the MAUI application also stops the host (application).
+ ///
+ /// IHostApplicationBuilder.
+ /// The same IHostApplicationBuilder instance.
+ public static IHostApplicationBuilder UseMauiLifetime(this IHostApplicationBuilder hostBuilder)
+ {
+ ArgumentNullException.ThrowIfNull(hostBuilder);
+
+ TryRetrieveMauiContext(hostBuilder.Properties, out var mauiContext);
+ mauiContext.IsLifetimeLinked = true;
+ return hostBuilder;
+ }
+
+ ///
+ /// Configure an MAUI application.
+ ///
+ /// IHostBuilder.
+ /// Action to configure Maui.
+ /// A IHostBuilder.
+ public static IHostBuilder ConfigureMaui(this IHostBuilder hostBuilder, Action? configureDelegate = null)
+ {
+ ArgumentNullException.ThrowIfNull(hostBuilder);
+
+ var mauiBuilder = new MauiBuilder();
+ configureDelegate?.Invoke(mauiBuilder);
+
+ if (mauiBuilder.ApplicationType != null && mauiBuilder.Application == null && Application.Current?.GetType() == mauiBuilder.ApplicationType)
+ {
+ mauiBuilder.Application = Application.Current;
+ }
+
+ hostBuilder.ConfigureServices((_, serviceCollection) =>
+ {
+ if (!TryRetrieveMauiContext(hostBuilder.Properties, out var mauiContext))
+ {
+ serviceCollection
+ .AddSingleton(mauiContext)
+ .AddSingleton(serviceProvider => new MauiThread(serviceProvider))
+ .AddHostedService();
+ }
+
+ mauiBuilder.ConfigureContextAction?.Invoke(mauiContext);
+ });
+
+ if (mauiBuilder.ApplicationType != null)
+ {
+ // Check if the registered application does inherit Microsoft.Maui.Controls.Application
+ var baseApplicationType = typeof(Application);
+ if (!baseApplicationType.IsAssignableFrom(mauiBuilder.ApplicationType))
+ {
+ throw new ArgumentException("The registered Application type must inherit Microsoft.Maui.Controls.Application", nameof(configureDelegate));
+ }
+
+ hostBuilder.ConfigureServices((_, serviceCollection) =>
+ {
+ if (mauiBuilder.Application != null)
+ {
+ // Add existing Application
+ serviceCollection.AddSingleton(mauiBuilder.ApplicationType, mauiBuilder.Application);
+ }
+ else
+ {
+ serviceCollection.AddSingleton(mauiBuilder.ApplicationType);
+ }
+
+ if (mauiBuilder.ApplicationType != baseApplicationType)
+ {
+ serviceCollection.AddSingleton(serviceProvider => (Application)serviceProvider.GetRequiredService(mauiBuilder.ApplicationType));
+ }
+ });
+ }
+
+ if (mauiBuilder.PageTypes.Count > 0)
+ {
+ hostBuilder.ConfigureServices(serviceCollection =>
+ {
+ foreach (var mauiPageType in mauiBuilder.PageTypes)
+ {
+ serviceCollection.AddSingleton(mauiPageType);
+
+ // Check if it also implements IMauiShell so we can register it as this
+ var shellInterfaceType = typeof(IMauiShell);
+ if (shellInterfaceType.IsAssignableFrom(mauiPageType))
+ {
+ serviceCollection.AddSingleton(shellInterfaceType, serviceProvider => serviceProvider.GetRequiredService(mauiPageType));
+ }
+ }
+ });
+ }
+
+ return hostBuilder;
+ }
+
+ ///
+ /// Configure a MAUI application for the new builder API.
+ ///
+ /// The IHostApplicationBuilder.
+ /// Action to configure Maui.
+ /// The same IHostApplicationBuilder instance.
+ public static IHostApplicationBuilder ConfigureMaui(this IHostApplicationBuilder hostBuilder, Action? configureDelegate = null)
+ {
+ ArgumentNullException.ThrowIfNull(hostBuilder);
+
+ var mauiBuilder = new MauiBuilder();
+ configureDelegate?.Invoke(mauiBuilder);
+
+ if (mauiBuilder.ApplicationType != null && mauiBuilder.Application == null && Application.Current?.GetType() == mauiBuilder.ApplicationType)
+ {
+ mauiBuilder.Application = Application.Current;
+ }
+
+ if (!TryRetrieveMauiContext(hostBuilder.Properties, out var mauiContext))
+ {
+ hostBuilder.Services
+ .AddSingleton(mauiContext)
+ .AddSingleton(serviceProvider => new MauiThread(serviceProvider))
+ .AddHostedService();
+ }
+
+ mauiBuilder.ConfigureContextAction?.Invoke(mauiContext);
+
+ if (mauiBuilder.ApplicationType != null)
+ {
+ // Check if the registered application does inherit Microsoft.Maui.Controls.Application
+ var baseApplicationType = typeof(Application);
+ if (!baseApplicationType.IsAssignableFrom(mauiBuilder.ApplicationType))
+ {
+ throw new ArgumentException("The registered Application type must inherit Microsoft.Maui.Controls.Application", nameof(configureDelegate));
+ }
+
+ if (mauiBuilder.Application != null)
+ {
+ // Add existing Application
+ hostBuilder.Services.AddSingleton(mauiBuilder.ApplicationType, mauiBuilder.Application);
+ }
+ else
+ {
+ hostBuilder.Services.AddSingleton(mauiBuilder.ApplicationType);
+ }
+
+ if (mauiBuilder.ApplicationType != baseApplicationType)
+ {
+ hostBuilder.Services.AddSingleton(serviceProvider => (Application)serviceProvider.GetRequiredService(mauiBuilder.ApplicationType));
+ }
+ }
+
+ if (mauiBuilder.PageTypes.Count > 0)
+ {
+ foreach (var mauiPageType in mauiBuilder.PageTypes)
+ {
+ hostBuilder.Services.AddSingleton(mauiPageType);
+
+ // Check if it also implements IMauiShell so we can register it as this
+ var shellInterfaceType = typeof(IMauiShell);
+ if (shellInterfaceType.IsAssignableFrom(mauiPageType))
+ {
+ hostBuilder.Services.AddSingleton(shellInterfaceType, serviceProvider => serviceProvider.GetRequiredService(mauiPageType));
+ }
+ }
+ }
+
+ var app = mauiBuilder.MauiAppBuilder.Build();
+ hostBuilder.Services.AddSingleton(app);
+
+ return hostBuilder;
+ }
+
+ ///
+ /// Specify a shell, the primary Page, to start.
+ ///
+ /// IHostBuilder.
+ /// Type for the shell, must derive from Page and implement IMauiShell.
+ /// A IHostBuilder.
+ public static IHostBuilder? ConfigureMauiShell(this IHostBuilder hostBuilder)
+ where TShell : Page, IMauiShell
+ => hostBuilder?.ConfigureMaui(maui => maui.AddSingletonPage());
+
+ ///
+ /// Specify a shell, the primary Page, to start. (IHostApplicationBuilder).
+ ///
+ /// IHostApplicationBuilder.
+ /// Type for the shell, must derive from Page and implement IMauiShell.
+ /// The same IHostApplicationBuilder instance.
+ public static IHostApplicationBuilder ConfigureMauiShell(this IHostApplicationBuilder hostBuilder)
+ where TShell : Page, IMauiShell
+ => hostBuilder.ConfigureMaui(maui => maui.AddSingletonPage());
+
+ ///
+ /// Helper method to retrieve the IMauiContext.
+ ///
+ /// IDictionary.
+ /// IMauiContext out value.
+ /// bool if there was already an IMauiContext.
+ private static bool TryRetrieveMauiContext(this IDictionary