diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 981af17fef54..b4c179a3cd54 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21404.1", + "version": "1.0.0-prerelease.21425.2", "commands": [ "xharness" ] diff --git a/eng/Microsoft.Extensions.targets b/eng/Microsoft.Extensions.targets index 7510be16cb19..d15d1b07c10d 100644 --- a/eng/Microsoft.Extensions.targets +++ b/eng/Microsoft.Extensions.targets @@ -3,19 +3,7 @@ <_UseNet6Packages Condition=" '$(TargetFramework.Contains(net6.0))' == 'true' or '$(PackageType)' == 'DotnetPlatform' ">true - <_MicrosoftHostingVersion>5.0.0 4.7.0 - (_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - 5.0.1 - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) - $(_MicrosoftHostingVersion) @@ -69,6 +57,10 @@ Update="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" /> + https://github.com/dotnet/runtime 14b34eb02bc8969b77c0d3a1e39fb38f450625cf + + https://github.com/dotnet/runtime + 58efa4b79751a2dad08d9bf7ca67930f8160afe3 + https://github.com/dotnet/runtime 14b34eb02bc8969b77c0d3a1e39fb38f450625cf diff --git a/eng/Versions.props b/eng/Versions.props index d2ac516e10de..4bba8896a949 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -10,6 +10,7 @@ 12.0.100-rc.1.521 6.0.0-rc.1.21417.1 6.0.0-rc.1.21417.1 + 6.0.0-rc.1.21417.1 6.0.0-rc.1.21417.1 6.0.0-rc.1.21417.1 6.0.0-rc.1.21417.1 diff --git a/src/BlazorWebView/src/Maui/BlazorWebViewRegistrationExtensions.cs b/src/BlazorWebView/src/Maui/BlazorWebViewRegistrationExtensions.cs index 30ee76f0668e..ad927896205b 100644 --- a/src/BlazorWebView/src/Maui/BlazorWebViewRegistrationExtensions.cs +++ b/src/BlazorWebView/src/Maui/BlazorWebViewRegistrationExtensions.cs @@ -1,18 +1,19 @@ using System; +using Microsoft.Maui; using Microsoft.Maui.Hosting; namespace Microsoft.AspNetCore.Components.WebView.Maui { public static class BlazorWebViewRegistrationExtensions { - public static TAppHostBuilder RegisterBlazorMauiWebView(this TAppHostBuilder appHostBuilder) where TAppHostBuilder : IAppHostBuilder + public static MauiAppBuilder RegisterBlazorMauiWebView(this MauiAppBuilder appHostBuilder) { if (appHostBuilder is null) { throw new ArgumentNullException(nameof(appHostBuilder)); } - appHostBuilder.ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()); + appHostBuilder.ConfigureMauiHandlers(handlers => handlers.AddHandler()); return appHostBuilder; } diff --git a/src/Compatibility/ControlGallery/src/Core/Startup.cs b/src/Compatibility/ControlGallery/src/Core/Startup.cs index 308c1f1dd206..3fc4bf7e8a06 100644 --- a/src/Compatibility/ControlGallery/src/Core/Startup.cs +++ b/src/Compatibility/ControlGallery/src/Core/Startup.cs @@ -6,13 +6,20 @@ namespace Microsoft.Maui.Controls.Compatibility.ControlGallery { - public class Startup : IStartup + public static class MauiProgram { internal static bool UseBlazor = false; - public virtual void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { - appBuilder + var builder = CreateMauiAppBuilder(); + return builder.Build(); + } + + public static MauiAppBuilder CreateMauiAppBuilder() + { + var builder = MauiApp.CreateBuilder(); + builder .UseMauiApp() .ConfigureMauiHandlers(handlers => { @@ -26,14 +33,14 @@ public virtual void Configure(IAppHostBuilder appBuilder) { fonts.AddCompatibilityFonts(Device.GetAssemblies()); }) - .ConfigureServices(services => - { - DependencyService.Register(Device.GetAssemblies()); - }) .ConfigureEffects(effects => { effects.AddCompatibilityEffects(Device.GetAssemblies()); }); + + DependencyService.Register(Device.GetAssemblies()); + + return builder; } } } diff --git a/src/Compatibility/ControlGallery/src/WinUI/App.xaml.cs b/src/Compatibility/ControlGallery/src/WinUI/App.xaml.cs index 19e91c85a86c..4276fb0079c0 100644 --- a/src/Compatibility/ControlGallery/src/WinUI/App.xaml.cs +++ b/src/Compatibility/ControlGallery/src/WinUI/App.xaml.cs @@ -16,6 +16,6 @@ public App() InitializeComponent(); } - protected override IStartup OnCreateStartup() => new WinUIStartup(); + protected override MauiApp CreateMauiApp() => WinUIMauiProgram.CreateMauiApp(); } } diff --git a/src/Compatibility/ControlGallery/src/WinUI/WinUIStartup.cs b/src/Compatibility/ControlGallery/src/WinUI/WinUIStartup.cs index 01e3c0edfd15..e27bca68c3fa 100644 --- a/src/Compatibility/ControlGallery/src/WinUI/WinUIStartup.cs +++ b/src/Compatibility/ControlGallery/src/WinUI/WinUIStartup.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Reflection.PortableExecutable; using Microsoft.Maui.Controls.Compatibility.Platform.UWP; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Graphics; @@ -15,13 +16,13 @@ namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.WinUI { - public class WinUIStartup : Startup + public class WinUIMauiProgram { - public override void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { - base.Configure(appBuilder); + var builder = MauiProgram.CreateMauiAppBuilder(); - appBuilder.ConfigureLifecycleEvents(lifecycle => lifecycle + builder.ConfigureLifecycleEvents(lifecycle => lifecycle .AddWindows(windows => windows .OnLaunching((_, e) => { @@ -32,6 +33,8 @@ public override void Configure(IAppHostBuilder appBuilder) } }) .OnActivated(WinUIPageStartup.OnActivated))); + + return builder.Build(); } } diff --git a/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs b/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs index 7da514d17287..8504dc256440 100644 --- a/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs +++ b/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs @@ -6,6 +6,7 @@ using CoreGraphics; using Foundation; using UIKit; +using Microsoft.Maui; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Compatibility.ControlGallery.iOS; using Microsoft.Maui.Controls.Compatibility; @@ -100,8 +101,10 @@ public string GetTestCloudDevice() } [Register("AppDelegate")] - public partial class AppDelegate : MauiUIApplicationDelegate + public partial class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions) { UISwitch.Appearance.OnTintColor = UIColor.Red; diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs index 7c89f1fb3538..ac32a1b5b4f0 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.Controls.Hosting { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCompatibilityLifecycleEvents(this MauiAppBuilder builder) => builder.ConfigureLifecycleEvents(events => events.AddAndroid(OnConfigureLifeCycle)); static void OnConfigureLifeCycle(IAndroidLifecycleBuilder android) diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs index f0ac34387ee1..298c043e9681 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.Controls.Hosting { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCompatibilityLifecycleEvents(this MauiAppBuilder builder) => builder; } } diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs index 8308f1fb5536..d0d1747efc3e 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs @@ -17,7 +17,7 @@ namespace Microsoft.Maui.Controls.Hosting { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCompatibilityLifecycleEvents(this MauiAppBuilder builder) => builder.ConfigureLifecycleEvents(events => events.AddWindows(OnConfigureLifeCycle)); static void OnConfigureLifeCycle(IWindowsLifecycleBuilder windows) diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs index 31f1755bf20f..1ad2e6a24c10 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs @@ -1,6 +1,7 @@ #nullable enable using System; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Maui.Controls.Compatibility; using Microsoft.Maui.Controls.Shapes; @@ -40,36 +41,25 @@ namespace Microsoft.Maui.Controls.Hosting { - public static partial class AppHostBuilderExtensions + public static class MauiAppBuilderExtensions { - public static IAppHostBuilder UseMauiApp(this IAppHostBuilder builder) + public static MauiAppBuilder UseMauiApp(this MauiAppBuilder builder) where TApp : class, IApplication { - builder.ConfigureServices((context, collection) => - { - collection.AddSingleton(); - }); - + builder.Services.TryAddSingleton(); builder.SetupDefaults(); - return builder; } - public static IAppHostBuilder UseMauiApp(this IAppHostBuilder builder, Func implementationFactory) + public static MauiAppBuilder UseMauiApp(this MauiAppBuilder builder, Func implementationFactory) where TApp : class, IApplication { - builder.ConfigureServices((context, collection) => - { - collection.AddSingleton(implementationFactory); - }); - + builder.Services.TryAddSingleton(implementationFactory); builder.SetupDefaults(); - return builder; } - - static IAppHostBuilder ConfigureImageSourceHandlers(this IAppHostBuilder builder) + static MauiAppBuilder ConfigureImageSourceHandlers(this MauiAppBuilder builder) { builder.ConfigureImageSources(services => { @@ -82,7 +72,7 @@ static IAppHostBuilder ConfigureImageSourceHandlers(this IAppHostBuilder builder return builder; } - static IAppHostBuilder SetupDefaults(this IAppHostBuilder builder) + static MauiAppBuilder SetupDefaults(this MauiAppBuilder builder) { builder.ConfigureCompatibilityLifecycleEvents(); builder.ConfigureImageSourceHandlers(); @@ -180,16 +170,30 @@ static IAppHostBuilder SetupDefaults(this IAppHostBuilder builder) VisualElement.RemapForControls(); Label.RemapForControls(); Button.RemapForControls(); + }); - }) - .ConfigureServices(); + builder.AddMauiCompat(); return builder; } - class MauiCompatBuilder : IMauiServiceBuilder + private static MauiAppBuilder AddMauiCompat(this MauiAppBuilder builder) { - public void Configure(HostBuilderContext context, IServiceProvider services) +#if __IOS__ || MACCATALYST + builder.Services.TryAddSingleton(NativeGraphicsService.Instance); +#elif __ANDROID__ + builder.Services.TryAddSingleton(NativeGraphicsService.Instance); +#elif WINDOWS + builder.Services.TryAddSingleton(W2DGraphicsService.Instance); +#endif + + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient()); + return builder; + } + + class MauiCompatInitializer : IMauiInitializeService + { + public void Initialize(IServiceProvider services) { #if __ANDROID__ || __IOS__ || WINDOWS || MACCATALYST CompatServiceProvider.SetServiceProvider(services); @@ -217,17 +221,6 @@ public void Configure(HostBuilderContext context, IServiceProvider services) #endif } - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { -#if __IOS__ || MACCATALYST - services.AddSingleton(NativeGraphicsService.Instance); -#elif __ANDROID__ - services.AddSingleton(NativeGraphicsService.Instance); -#elif WINDOWS - services.AddSingleton(W2DGraphicsService.Instance); -#endif - } - #if WINDOWS static void AddLibraryResources(string key, string uri) { diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs index b9958f522dc5..1a064d72e303 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.Controls.Hosting { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCompatibilityLifecycleEvents(this MauiAppBuilder builder) => builder.ConfigureLifecycleEvents(events => events.AddiOS(OnConfigureLifeCycle)); static void OnConfigureLifeCycle(IiOSLifecycleBuilder iOS) diff --git a/src/Controls/samples/Controls.Sample.Droid/MainApplication.cs b/src/Controls/samples/Controls.Sample.Droid/MainApplication.cs index 4462822737ae..90ebd5fddfa2 100644 --- a/src/Controls/samples/Controls.Sample.Droid/MainApplication.cs +++ b/src/Controls/samples/Controls.Sample.Droid/MainApplication.cs @@ -7,10 +7,12 @@ namespace Maui.Controls.Sample.Droid { [Application] - public class MainApplication : MauiApplication + public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } -} \ No newline at end of file +} diff --git a/src/Controls/samples/Controls.Sample.MacCatalyst/AppDelegate.cs b/src/Controls/samples/Controls.Sample.MacCatalyst/AppDelegate.cs index 70e3b7ea96d3..4069dfbf008c 100644 --- a/src/Controls/samples/Controls.Sample.MacCatalyst/AppDelegate.cs +++ b/src/Controls/samples/Controls.Sample.MacCatalyst/AppDelegate.cs @@ -11,7 +11,8 @@ namespace Sample.MacCatalyst { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.Profiling/Platforms/Android/MainApplication.cs b/src/Controls/samples/Controls.Sample.Profiling/Platforms/Android/MainApplication.cs index d12d1aea8b30..ca74a7817308 100644 --- a/src/Controls/samples/Controls.Sample.Profiling/Platforms/Android/MainApplication.cs +++ b/src/Controls/samples/Controls.Sample.Profiling/Platforms/Android/MainApplication.cs @@ -6,10 +6,12 @@ namespace Maui.Controls.Sample.Profiling { [Application] - public class MainApplication : MauiApplication + public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.Profiling/Platforms/MacCatalyst/AppDelegate.cs b/src/Controls/samples/Controls.Sample.Profiling/Platforms/MacCatalyst/AppDelegate.cs index a1cbd96a99f4..067a47afe8d8 100644 --- a/src/Controls/samples/Controls.Sample.Profiling/Platforms/MacCatalyst/AppDelegate.cs +++ b/src/Controls/samples/Controls.Sample.Profiling/Platforms/MacCatalyst/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Maui.Controls.Sample.Profiling { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.Profiling/Platforms/iOS/AppDelegate.cs b/src/Controls/samples/Controls.Sample.Profiling/Platforms/iOS/AppDelegate.cs index a1cbd96a99f4..067a47afe8d8 100644 --- a/src/Controls/samples/Controls.Sample.Profiling/Platforms/iOS/AppDelegate.cs +++ b/src/Controls/samples/Controls.Sample.Profiling/Platforms/iOS/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Maui.Controls.Sample.Profiling { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.Profiling/Startup.cs b/src/Controls/samples/Controls.Sample.Profiling/Startup.cs index a3ae6d507a48..c309ae512252 100644 --- a/src/Controls/samples/Controls.Sample.Profiling/Startup.cs +++ b/src/Controls/samples/Controls.Sample.Profiling/Startup.cs @@ -7,16 +7,20 @@ namespace Maui.Controls.Sample.Profiling { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { - appBuilder + var builder = MauiApp.CreateBuilder(); + + builder .UseMauiApp() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); + + return builder.Build(); } } } diff --git a/src/Controls/samples/Controls.Sample.SingleProject/MainWindow.cs b/src/Controls/samples/Controls.Sample.SingleProject/MainWindow.cs index a62a390aa430..c1f991069987 100644 --- a/src/Controls/samples/Controls.Sample.SingleProject/MainWindow.cs +++ b/src/Controls/samples/Controls.Sample.SingleProject/MainWindow.cs @@ -5,7 +5,7 @@ namespace Maui.Controls.Sample.SingleProject { public class MainWindow : Window { - public MainWindow() : base(Startup.UseBlazor ? new BlazorPage() : new MainPage()) + public MainWindow() : base(MauiProgram.UseBlazor ? new BlazorPage() : new MainPage()) { } } diff --git a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Android/MainApplication.cs b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Android/MainApplication.cs index 2e3b3e23ca5d..c51e98435125 100644 --- a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Android/MainApplication.cs +++ b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Android/MainApplication.cs @@ -6,10 +6,12 @@ namespace Maui.Controls.Sample.SingleProject { [Application] - public class MainApplication : MauiApplication + public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/MacCatalyst/AppDelegate.cs b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/MacCatalyst/AppDelegate.cs index 96ec576cb749..f27e234b7104 100644 --- a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/MacCatalyst/AppDelegate.cs +++ b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/MacCatalyst/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Maui.Controls.Sample.SingleProject { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Windows/App.xaml.cs b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Windows/App.xaml.cs index a52fe031f12b..e5c785008aa6 100644 --- a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Windows/App.xaml.cs +++ b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/Windows/App.xaml.cs @@ -22,7 +22,8 @@ public App() } } - public class MiddleApp : MauiWinUIApplication + public class MiddleApp : MauiWinUIApplication { - } + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } } diff --git a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/iOS/AppDelegate.cs b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/iOS/AppDelegate.cs index 96ec576cb749..f27e234b7104 100644 --- a/src/Controls/samples/Controls.Sample.SingleProject/Platforms/iOS/AppDelegate.cs +++ b/src/Controls/samples/Controls.Sample.SingleProject/Platforms/iOS/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Maui.Controls.Sample.SingleProject { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.SingleProject/Startup.cs b/src/Controls/samples/Controls.Sample.SingleProject/Startup.cs index 1ba1c1d6dae6..09ae9b7b3e86 100644 --- a/src/Controls/samples/Controls.Sample.SingleProject/Startup.cs +++ b/src/Controls/samples/Controls.Sample.SingleProject/Startup.cs @@ -1,31 +1,27 @@ using Microsoft.Maui; -using Microsoft.Maui.Controls.Compatibility; -using Microsoft.Maui.Hosting; using Microsoft.AspNetCore.Components.WebView.Maui; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Controls.Hosting; namespace Maui.Controls.Sample.SingleProject { - public class Startup : IStartup + public static class MauiProgram { internal static bool UseBlazor = false; - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { + var appBuilder = MauiApp.CreateBuilder(); appBuilder - .RegisterBlazorMauiWebView() .UseMauiApp(); if (UseBlazor) - { - appBuilder.UseMicrosoftExtensionsServiceProviderFactory(); // Blazor requires service scopes, which are supported only with Microsoft.Extensions.DependencyInjection - appBuilder - .ConfigureServices(services => - { - services.AddBlazorWebView(); - }); + { + appBuilder.RegisterBlazorMauiWebView(); + appBuilder.Services.AddBlazorWebView(); } + + return appBuilder.Build(); } } } diff --git a/src/Controls/samples/Controls.Sample.WinUI/App.xaml.cs b/src/Controls/samples/Controls.Sample.WinUI/App.xaml.cs index 874b0fc54006..935ad7ace227 100644 --- a/src/Controls/samples/Controls.Sample.WinUI/App.xaml.cs +++ b/src/Controls/samples/Controls.Sample.WinUI/App.xaml.cs @@ -9,6 +9,6 @@ public App() InitializeComponent(); } - protected override IStartup OnCreateStartup() => new Startup(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.iOS/AppDelegate.cs b/src/Controls/samples/Controls.Sample.iOS/AppDelegate.cs index cfcc70970938..b500dfdad512 100644 --- a/src/Controls/samples/Controls.Sample.iOS/AppDelegate.cs +++ b/src/Controls/samples/Controls.Sample.iOS/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Maui.Controls.Sample.iOS { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryAppHostBuilderExtensions.cs b/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryAppHostBuilderExtensions.cs index a864244d7e44..52aab9565362 100644 --- a/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryAppHostBuilderExtensions.cs +++ b/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryAppHostBuilderExtensions.cs @@ -1,13 +1,21 @@ using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Maui; using Microsoft.Maui.Hosting; namespace Maui.Controls.Sample.Controls { static class BordelessEntryAppHostBuilderExtensions { - public static IAppHostBuilder UseBordelessEntry(this IAppHostBuilder builder, Action configureDelegate = null) + public static MauiAppBuilder UseBordelessEntry(this MauiAppBuilder builder, Action configureDelegate = null) { - builder.ConfigureServices((ctx, red) => configureDelegate?.Invoke(red)); + builder.Services.TryAddSingleton(); + + if (configureDelegate != null) + { + builder.Services.AddSingleton(new BorderlessEntryRegistration(configureDelegate)); + } return builder; } diff --git a/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryServiceBuilder.cs b/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryServiceBuilder.cs index 468dc71bdca0..3bfd555225c4 100644 --- a/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryServiceBuilder.cs +++ b/src/Controls/samples/Controls.Sample/Controls/BordelessEntry/BordelessEntryServiceBuilder.cs @@ -7,10 +7,25 @@ namespace Maui.Controls.Sample.Controls { - class BordelessEntryServiceBuilder : IMauiServiceBuilder + internal class BorderlessEntryRegistration { - static IMauiHandlersCollection HandlersCollection; - static readonly Dictionary PendingHandlers = new(); + private readonly Action _builderAction; + + public BorderlessEntryRegistration(Action builderAction) + { + _builderAction = builderAction; + } + + internal void RunBuilderAction(BordelessEntryServiceBuilder builder) + { + _builderAction(builder); + } + } + + class BordelessEntryServiceBuilder + { + internal static IMauiHandlersCollection HandlersCollection; + internal static readonly Dictionary PendingHandlers = new(); public static void TryAddHandler() where TType : IView @@ -21,21 +36,35 @@ public static void TryAddHandler() else HandlersCollection.TryAddHandler(); } + } + + class BorderlessEntryInitializer : IMauiInitializeService + { + private readonly IEnumerable _borderlessEntryRegistrations; - void IMauiServiceBuilder.ConfigureServices(HostBuilderContext context, IServiceCollection services) + public BorderlessEntryInitializer(IEnumerable borderlessEntryRegistrations) { - // No-op + _borderlessEntryRegistrations = borderlessEntryRegistrations; } - void IMauiServiceBuilder.Configure(HostBuilderContext context, IServiceProvider services) + public void Initialize(IServiceProvider services) { - HandlersCollection ??= services.GetRequiredService().GetCollection(); + var essentialsBuilder = new BordelessEntryServiceBuilder(); + if (_borderlessEntryRegistrations != null) + { + foreach (var essentialsRegistration in _borderlessEntryRegistrations) + { + essentialsRegistration.RunBuilderAction(essentialsBuilder); + } + } + + BordelessEntryServiceBuilder.HandlersCollection ??= services.GetRequiredService().GetCollection(); - if (PendingHandlers.Count > 0) + if (BordelessEntryServiceBuilder.PendingHandlers.Count > 0) { - HandlersCollection.TryAddHandlers(PendingHandlers); - PendingHandlers.Clear(); + BordelessEntryServiceBuilder.HandlersCollection.TryAddHandlers(BordelessEntryServiceBuilder.PendingHandlers); + BordelessEntryServiceBuilder.PendingHandlers.Clear(); } } } -} \ No newline at end of file +} diff --git a/src/Controls/samples/Controls.Sample/Extensions/EssentialsExtensions.cs b/src/Controls/samples/Controls.Sample/Extensions/EssentialsExtensions.cs index 136ac74ed9a5..85fcbf6bfa19 100644 --- a/src/Controls/samples/Controls.Sample/Extensions/EssentialsExtensions.cs +++ b/src/Controls/samples/Controls.Sample/Extensions/EssentialsExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Maui.Hosting; @@ -10,18 +11,14 @@ namespace Microsoft.Maui.Essentials { public static class EssentialsExtensions { - public static IAppHostBuilder ConfigureEssentials(this IAppHostBuilder builder, Action configureDelegate = null) + public static MauiAppBuilder ConfigureEssentials(this MauiAppBuilder builder, Action configureDelegate = null) { - if (configureDelegate == null) - builder.ConfigureEssentials((Action)null); - else - builder.ConfigureEssentials((_, essentials) => configureDelegate(essentials)); - - return builder; - } + if (configureDelegate != null) + { + builder.Services.AddSingleton(new EssentialsRegistration(configureDelegate)); + } + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient()); - public static IAppHostBuilder ConfigureEssentials(this IAppHostBuilder builder, Action configureDelegate = null) - { builder.ConfigureLifecycleEvents(life => { #if __ANDROID__ @@ -67,73 +64,59 @@ public static IAppHostBuilder ConfigureEssentials(this IAppHostBuilder builder, #endif }); - if (configureDelegate != null) - builder.ConfigureServices(configureDelegate); - return builder; } public static IEssentialsBuilder AddAppAction(this IEssentialsBuilder essentials, string id, string title, string subtitle = null, string icon = null) => essentials.AddAppAction(new AppAction(id, title, subtitle, icon)); - class EssentialsBuilder : IEssentialsBuilder, IMauiServiceBuilder + internal class EssentialsRegistration { - readonly List _appActions = new List(); - Action _appActionHandlers; - bool _trackVersions; + private readonly Action _registerEssentials; -#pragma warning disable CS0414 // Remove unread private members - bool _useLegaceSecureStorage; - string _mapServiceToken; -#pragma warning restore CS0414 // Remove unread private members - - public IEssentialsBuilder UseMapServiceToken(string token) + public EssentialsRegistration(Action registerEssentials) { - _mapServiceToken = token; - return this; + _registerEssentials = registerEssentials; } - public IEssentialsBuilder AddAppAction(AppAction appAction) + internal void RegisterEssentialsOptions(IEssentialsBuilder essentials) { - _appActions.Add(appAction); - return this; - } - - public IEssentialsBuilder OnAppAction(Action action) - { - _appActionHandlers += action; - return this; + _registerEssentials(essentials); } + } - public IEssentialsBuilder UseVersionTracking() - { - _trackVersions = true; - return this; - } + class EssentialsInitializer : IMauiInitializeService + { + private readonly IEnumerable _essentialsRegistrations; + private EssentialsBuilder _essentialsBuilder; - public IEssentialsBuilder UseLegacySecureStorage() + public EssentialsInitializer(IEnumerable essentialsRegistrations) { - _useLegaceSecureStorage = true; - return this; + _essentialsRegistrations = essentialsRegistrations; } - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) + public async void Initialize(IServiceProvider services) { - } + _essentialsBuilder = new EssentialsBuilder(); + if (_essentialsRegistrations != null) + { + foreach (var essentialsRegistration in _essentialsRegistrations) + { + essentialsRegistration.RegisterEssentialsOptions(_essentialsBuilder); + } + } - public async void Configure(HostBuilderContext context, IServiceProvider services) - { #if WINDOWS - Platform.MapServiceToken = _mapServiceToken; + Platform.MapServiceToken = _essentialsBuilder.MapServiceToken; #elif __ANDROID__ - SecureStorage.LegacyKeyHashFallback = _useLegaceSecureStorage; + SecureStorage.LegacyKeyHashFallback = _essentialsBuilder.UseLegaceSecureStorage; #endif AppActions.OnAppAction += HandleOnAppAction; try { - await AppActions.SetAsync(_appActions); + await AppActions.SetAsync(_essentialsBuilder.AppActions); } catch (FeatureNotSupportedException ex) { @@ -142,13 +125,55 @@ public async void Configure(HostBuilderContext context, IServiceProvider service .LogError(ex, "App Actions are not supported on this platform."); } - if (_trackVersions) + if (_essentialsBuilder.TrackVersions) VersionTracking.Track(); } void HandleOnAppAction(object sender, AppActionEventArgs e) { - _appActionHandlers?.Invoke(e.AppAction); + _essentialsBuilder.AppActionHandlers?.Invoke(e.AppAction); + } + } + + class EssentialsBuilder : IEssentialsBuilder + { + internal readonly List AppActions = new List(); + internal Action AppActionHandlers; + internal bool TrackVersions; + +#pragma warning disable CS0414 // Remove unread private members + internal bool UseLegaceSecureStorage; + internal string MapServiceToken; +#pragma warning restore CS0414 // Remove unread private members + + public IEssentialsBuilder UseMapServiceToken(string token) + { + MapServiceToken = token; + return this; + } + + public IEssentialsBuilder AddAppAction(AppAction appAction) + { + AppActions.Add(appAction); + return this; + } + + public IEssentialsBuilder OnAppAction(Action action) + { + AppActionHandlers += action; + return this; + } + + public IEssentialsBuilder UseVersionTracking() + { + TrackVersions = true; + return this; + } + + public IEssentialsBuilder UseLegacySecureStorage() + { + UseLegaceSecureStorage = true; + return this; } } } diff --git a/src/Controls/samples/Controls.Sample/Startup.cs b/src/Controls/samples/Controls.Sample/Startup.cs index 91b91498256c..11e183e8d03b 100644 --- a/src/Controls/samples/Controls.Sample/Startup.cs +++ b/src/Controls/samples/Controls.Sample/Startup.cs @@ -24,14 +24,17 @@ namespace Maui.Controls.Sample { public class CustomButton : Button { } - public class Startup : IStartup + public static class MauiProgram { enum PageType { Main, Blazor, Shell, Template } - readonly PageType _pageType = PageType.Main; + readonly static PageType _pageType = PageType.Main; - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { + var appBuilder = MauiApp.CreateBuilder(); + appBuilder.UseMauiApp(); + var services = appBuilder.Services; appBuilder .ConfigureMauiHandlers(handlers => @@ -48,7 +51,6 @@ public void Configure(IAppHostBuilder appBuilder) #endif }); - // Use a "third party" library that brings in a massive amount of controls appBuilder.UseBordelessEntry(); appBuilder.ConfigureEffects(builder => @@ -60,41 +62,34 @@ public void Configure(IAppHostBuilder appBuilder) appBuilder.EnableHotReload(); #endif - appBuilder - .ConfigureAppConfiguration(config => - { - config.AddInMemoryCollection(new Dictionary + appBuilder.Configuration.AddInMemoryCollection( + new Dictionary { {"MyKey", "Dictionary MyKey Value"}, {":Title", "Dictionary_Title"}, {"Position:Name", "Dictionary_Name" }, {"Logging:LogLevel:Default", "Warning"} }); - }); #if NET6_0_OR_GREATER appBuilder .RegisterBlazorMauiWebView(); + services.AddBlazorWebView(); #endif - appBuilder - .ConfigureServices(services => - { - services.AddLogging(logging => - { + services.AddLogging(logging => + { #if WINDOWS - logging.AddDebug(); + logging.AddDebug(); #else - logging.AddConsole(); + logging.AddConsole(); #endif - }); + }); - services.AddSingleton(); - services.AddTransient(); + services.AddSingleton(); + services.AddTransient(); -#if NET6_0_OR_GREATER - services.AddBlazorWebView(); -#endif + services.AddTransient(); services.AddTransient( serviceType: typeof(Page), @@ -112,8 +107,7 @@ public void Configure(IAppHostBuilder appBuilder) _ => throw new Exception(), }); - services.AddTransient(); - }) + appBuilder .ConfigureFonts(fonts => { fonts.AddFont("Dokdo-Regular.ttf", "Dokdo"); @@ -218,6 +212,8 @@ static bool LogEvent(string eventName, string type = null) return true; } }); + + return appBuilder.Build(); } } } \ No newline at end of file diff --git a/src/Controls/src/Core/Controls.Core-net6.csproj b/src/Controls/src/Core/Controls.Core-net6.csproj index 1e9b50e3a9ff..75d5d3b9b772 100644 --- a/src/Controls/src/Core/Controls.Core-net6.csproj +++ b/src/Controls/src/Core/Controls.Core-net6.csproj @@ -21,6 +21,15 @@ + + + + + + + + + $(TargetsForTfmSpecificBuildOutput); diff --git a/src/Controls/src/Core/Controls.Core.csproj b/src/Controls/src/Core/Controls.Core.csproj index b3a52e955da0..35590f3cad30 100644 --- a/src/Controls/src/Core/Controls.Core.csproj +++ b/src/Controls/src/Core/Controls.Core.csproj @@ -32,6 +32,14 @@ + + + + + + + + high diff --git a/src/Controls/src/Core/Hosting/Effects/AppHostBuilderExtensions.cs b/src/Controls/src/Core/Hosting/Effects/AppHostBuilderExtensions.cs index 4bb6e931ea2e..60cd9d84303c 100644 --- a/src/Controls/src/Core/Hosting/Effects/AppHostBuilderExtensions.cs +++ b/src/Controls/src/Core/Hosting/Effects/AppHostBuilderExtensions.cs @@ -1,74 +1,90 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Controls.Platform; -using Microsoft.Maui.Hosting; namespace Microsoft.Maui.Controls.Hosting { public static partial class AppHostBuilderExtensions { - public static IAppHostBuilder ConfigureEffects(this IAppHostBuilder builder, Action configureDelegate) + public static MauiAppBuilder ConfigureEffects(this MauiAppBuilder builder, Action configureDelegate) { - builder.ConfigureServices(b => configureDelegate(b)); + builder.Services.TryAddSingleton(); + if (configureDelegate != null) + { + builder.Services.AddSingleton(new EffectsRegistration(configureDelegate)); + } + return builder; } + } - class EffectCollectionBuilder : IMauiServiceBuilder, IEffectsBuilder + internal class EffectsRegistration + { + private readonly Action _registerEffects; + + public EffectsRegistration(Action registerEffects) { - internal Dictionary> RegisteredEffects { get; } = new Dictionary>(); + _registerEffects = registerEffects; + } - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - services.AddSingleton(); - } + internal void AddEffects(IEffectsBuilder effects) + { + _registerEffects(effects); + } + } - public void Configure(HostBuilderContext context, IServiceProvider services) - { - var effectsProvider = services.GetRequiredService(); - effectsProvider.SetRegisteredEffects(RegisteredEffects); - } + internal class EffectCollectionBuilder : IEffectsBuilder + { + internal Dictionary> RegisteredEffects { get; } = new Dictionary>(); - public IEffectsBuilder Add() - where TEffect : RoutingEffect - where TPlatformEffect : PlatformEffect, new() + public IEffectsBuilder Add() + where TEffect : RoutingEffect + where TPlatformEffect : PlatformEffect, new() + { + RegisteredEffects.Add(typeof(TEffect), () => { - RegisteredEffects.Add(typeof(TEffect), () => - { - if (DependencyResolver.Resolve(typeof(TPlatformEffect)) is TPlatformEffect pe) - return pe; + if (DependencyResolver.Resolve(typeof(TPlatformEffect)) is TPlatformEffect pe) + return pe; - return new TPlatformEffect(); - }); - return this; - } + return new TPlatformEffect(); + }); + return this; + } - public IEffectsBuilder Add(Type TEffect, Type TPlatformEffect) + public IEffectsBuilder Add(Type TEffect, Type TPlatformEffect) + { + RegisteredEffects.Add(TEffect, () => { - RegisteredEffects.Add(TEffect, () => - { - return (PlatformEffect)DependencyResolver.ResolveOrCreate(TPlatformEffect); - }); + return (PlatformEffect)DependencyResolver.ResolveOrCreate(TPlatformEffect); + }); - return this; - } + return this; } } internal class EffectsFactory { - Dictionary> _registeredEffects; + private readonly Dictionary> _registeredEffects; - internal void SetRegisteredEffects(Dictionary> registeredEffects) + public EffectsFactory(IEnumerable effectsRegistrations) { - _registeredEffects = registeredEffects; + if (effectsRegistrations != null) + { + var effectsBuilder = new EffectCollectionBuilder(); + foreach (var effectRegistration in effectsRegistrations) + { + effectRegistration.AddEffects(effectsBuilder); + } + _registeredEffects = effectsBuilder.RegisteredEffects; + } } internal PlatformEffect CreateEffect(Effect fromEffect) { - if (_registeredEffects.TryGetValue(fromEffect.GetType(), out Func effectType)) + if (_registeredEffects != null && _registeredEffects.TryGetValue(fromEffect.GetType(), out Func effectType)) { return effectType(); } diff --git a/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs b/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs index 42719bc4f72b..9d4756a9f551 100644 --- a/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs +++ b/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs @@ -1,10 +1,5 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Maui.Controls; -using Microsoft.Maui.Controls.Core; using Microsoft.Maui.Controls.Hosting; -using Microsoft.Maui.Handlers; -using Microsoft.Maui.Hosting; -using NUnit; using NUnit.Framework; namespace Microsoft.Maui.Controls.Core.UnitTests @@ -15,34 +10,34 @@ public class HostBuilderAppTests [Test] public void UseMauiAppRegistersApp() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .UseMauiApp() .Build(); - var app = (ApplicationStub)host.Services.GetRequiredService(); + var app = (ApplicationStub)mauiApp.Services.GetRequiredService(); Assert.AreEqual("Default", app.Property); } [Test] public void UseMauiAppRegistersAppWithFactory() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .UseMauiApp(services => new ApplicationStub { Property = "Factory" }) .Build(); - var app = (ApplicationStub)host.Services.GetRequiredService(); + var app = (ApplicationStub)mauiApp.Services.GetRequiredService(); Assert.AreEqual("Factory", app.Property); } [Test] public void UseMauiAppRegistersSingleton() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .UseMauiApp() .Build(); - var app1 = host.Services.GetRequiredService(); - var app2 = host.Services.GetRequiredService(); + var app1 = mauiApp.Services.GetRequiredService(); + var app2 = mauiApp.Services.GetRequiredService(); Assert.AreEqual(app1, app2); } diff --git a/src/Controls/tests/Core.UnitTests/HostBuilderHandlerTests.cs b/src/Controls/tests/Core.UnitTests/HostBuilderHandlerTests.cs index a2aabaf0617a..438fc82d85b5 100644 --- a/src/Controls/tests/Core.UnitTests/HostBuilderHandlerTests.cs +++ b/src/Controls/tests/Core.UnitTests/HostBuilderHandlerTests.cs @@ -1,10 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Maui.Controls; -using Microsoft.Maui.Controls.Core; using Microsoft.Maui.Controls.Hosting; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; -using NUnit; using NUnit.Framework; namespace Microsoft.Maui.Controls.Core.UnitTests @@ -15,11 +12,14 @@ public class HostBuilderHandlerTests [Test] public void DefaultHandlersAreRegistered() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .UseMauiApp() .Build(); - var handler = host.Handlers.GetHandler(typeof(Button)); + Assert.NotNull(mauiApp.Services); + var handlers = mauiApp.Services.GetRequiredService(); + Assert.NotNull(handlers); + var handler = handlers.GetHandler(typeof(Button)); Assert.NotNull(handler); Assert.AreEqual(handler.GetType(), typeof(ButtonHandler)); @@ -28,12 +28,16 @@ public void DefaultHandlersAreRegistered() [Test] public void CanSpecifyHandler() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .UseMauiApp() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()) + .ConfigureMauiHandlers(handlers => handlers.AddHandler()) .Build(); - var specificHandler = host.Handlers.GetHandler(typeof(Button)); + Assert.NotNull(mauiApp.Services); + var handlers = mauiApp.Services.GetRequiredService(); + Assert.NotNull(handlers); + + var specificHandler = handlers.GetHandler(typeof(Button)); Assert.NotNull(specificHandler); Assert.AreEqual(specificHandler.GetType(), typeof(ButtonHandlerStub)); diff --git a/src/Core/src/Core-net6.csproj b/src/Core/src/Core-net6.csproj index 4deb2418348a..3aba3dec012c 100644 --- a/src/Core/src/Core-net6.csproj +++ b/src/Core/src/Core-net6.csproj @@ -14,7 +14,9 @@ + + diff --git a/src/Core/src/Core.csproj b/src/Core/src/Core.csproj index 2471bf934630..c2438863b939 100644 --- a/src/Core/src/Core.csproj +++ b/src/Core/src/Core.csproj @@ -8,8 +8,10 @@ - + + + diff --git a/src/Core/src/Core/BootstrapHostBuilder.cs b/src/Core/src/Core/BootstrapHostBuilder.cs new file mode 100644 index 000000000000..3bfdb4ae249b --- /dev/null +++ b/src/Core/src/Core/BootstrapHostBuilder.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Maui +{ + // This exists solely to bootstrap the configuration + internal class BootstrapHostBuilder : IHostBuilder + { + private readonly IServiceCollection _services; + private readonly List> _configureHostActions = new(); + private readonly List> _configureAppActions = new(); + private readonly List> _configureServicesActions = new(); + + private readonly List> _remainingOperations = new(); + + public BootstrapHostBuilder(IServiceCollection services, IDictionary properties) + { + _services = services; + + Properties = properties; + } + + public IDictionary Properties { get; } + + public IHost Build() + { + // HostingHostBuilderExtensions.ConfigureDefaults should never call this. + throw new InvalidOperationException(); + } + + public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) + { + _configureAppActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); + return this; + } + + public IHostBuilder ConfigureContainer(Action configureDelegate) + { + // This is not called by HostingHostBuilderExtensions.ConfigureDefaults currently, but that could change in the future. + // If this does get called in the future, it should be called again at a later stage on the ConfigureHostBuilder. + if (configureDelegate is null) + { + throw new ArgumentNullException(nameof(configureDelegate)); + } + + _remainingOperations.Add(hostBuilder => hostBuilder.ConfigureContainer(configureDelegate)); + return this; + } + + public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) + { + _configureHostActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); + return this; + } + + public IHostBuilder ConfigureServices(Action configureDelegate) + { + // HostingHostBuilderExtensions.ConfigureDefaults calls this via ConfigureLogging + _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); + return this; + } + + public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull + { + // This is not called by HostingHostBuilderExtensions.ConfigureDefaults currently, but that could change in the future. + // If this does get called in the future, it should be called again at a later stage on the ConfigureHostBuilder. + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + _remainingOperations.Add(hostBuilder => hostBuilder.UseServiceProviderFactory(factory)); + return this; + } + + public IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull + { + // HostingHostBuilderExtensions.ConfigureDefaults calls this via UseDefaultServiceProvider + // during the initial config stage. It should be called again later on the ConfigureHostBuilder. + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + _remainingOperations.Add(hostBuilder => hostBuilder.UseServiceProviderFactory(factory)); + return this; + } + + public HostBuilderContext RunDefaultCallbacks(ConfigurationManager configuration, HostBuilder innerBuilder) + { + var hostConfiguration = new ConfigurationManager(); + + foreach (var configureHostAction in _configureHostActions) + { + configureHostAction(hostConfiguration); + } + + // This is the hosting environment based on configuration we've seen so far. + var hostingEnvironment = new HostingEnvironment() + { + ApplicationName = hostConfiguration[HostDefaults.ApplicationKey], + EnvironmentName = hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production, + ContentRootPath = HostingEnvironment.ResolveContentRootPath(hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory), + }; + + hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(hostingEnvironment.ContentRootPath); + + var hostContext = new HostBuilderContext(Properties) + { + Configuration = hostConfiguration, + HostingEnvironment = hostingEnvironment, + }; + + // Split the host configuration and app configuration so that the + // subsequent callback don't get a chance to modify the host configuration. + configuration.SetBasePath(hostingEnvironment.ContentRootPath); + + // Chain the host configuration and app configuration together. + configuration.AddConfiguration(hostConfiguration, shouldDisposeConfiguration: true); + + // ConfigureAppConfiguration cannot modify the host configuration because doing so could + // change the environment, content root and application name which is not allowed at this stage. + foreach (var configureAppAction in _configureAppActions) + { + configureAppAction(hostContext, configuration); + } + + // Update the host context, everything from here sees the final + // app configuration + hostContext.Configuration = configuration; + + foreach (var configureServicesAction in _configureServicesActions) + { + configureServicesAction(hostContext, _services); + } + + foreach (var callback in _remainingOperations) + { + callback(innerBuilder); + } + + return hostContext; + } + + private class HostingEnvironment : IHostEnvironment + { + public string EnvironmentName { get; set; } = default!; + public string ApplicationName { get; set; } = default!; + public string ContentRootPath { get; set; } = default!; + public IFileProvider ContentRootFileProvider { get; set; } = default!; + + public static string ResolveContentRootPath(string contentRootPath, string basePath) + { + if (string.IsNullOrEmpty(contentRootPath)) + { + return basePath; + } + if (Path.IsPathRooted(contentRootPath)) + { + return contentRootPath; + } + return Path.Combine(Path.GetFullPath(basePath), contentRootPath); + } + } + } +} diff --git a/src/Core/src/Core/ConfigureHostBuilder.cs b/src/Core/src/Core/ConfigureHostBuilder.cs new file mode 100644 index 000000000000..39a1826ba85a --- /dev/null +++ b/src/Core/src/Core/ConfigureHostBuilder.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Microsoft.Maui +{ + /// + /// A non-buildable for . + /// Use to build the . + /// + public sealed class ConfigureHostBuilder : IHostBuilder + { + private readonly ConfigurationManager _configuration; + private readonly IServiceCollection _services; + private readonly HostBuilderContext _context; + + private readonly List> _operations = new(); + + internal ConfigureHostBuilder(HostBuilderContext context, ConfigurationManager configuration, IServiceCollection services) + { + _configuration = configuration; + _services = services; + _context = context; + } + + /// + public IDictionary Properties => _context.Properties; + + IHost IHostBuilder.Build() + { + throw new NotSupportedException($"Call {nameof(MauiAppBuilder)}.{nameof(MauiAppBuilder.Build)}() instead."); + } + + /// + public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) + { + // Run these immediately so that they are observable by the imperative code + configureDelegate(_context, _configuration); + return this; + } + + /// + public IHostBuilder ConfigureContainer(Action configureDelegate) + { + if (configureDelegate is null) + { + throw new ArgumentNullException(nameof(configureDelegate)); + } + + _operations.Add(b => b.ConfigureContainer(configureDelegate)); + return this; + } + + /// + public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) + { + var previousApplicationName = _configuration[HostDefaults.ApplicationKey]; + var previousContentRoot = _configuration[HostDefaults.ContentRootKey]; + var previousEnvironment = _configuration[HostDefaults.EnvironmentKey]; + + // Run these immediately so that they are observable by the imperative code + configureDelegate(_configuration); + + // Disallow changing any host settings this late in the cycle, the reasoning is that we've already loaded the default configuration + // and done other things based on environment name, application name or content root. + if (!string.Equals(previousApplicationName, _configuration[HostDefaults.ApplicationKey], StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException("The application name changed. Changing the host configuration is not supported"); + } + + if (!string.Equals(previousContentRoot, _configuration[HostDefaults.ContentRootKey], StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException("The content root changed. Changing the host configuration is not supported"); + } + + if (!string.Equals(previousEnvironment, _configuration[HostDefaults.EnvironmentKey], StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException("The environment changed. Changing the host configuration is not supported"); + } + + return this; + } + + /// + public IHostBuilder ConfigureServices(Action configureDelegate) + { + // Run these immediately so that they are observable by the imperative code + configureDelegate(_context, _services); + return this; + } + + /// + public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull + { + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + _operations.Add(b => b.UseServiceProviderFactory(factory)); + return this; + } + + /// + public IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull + { + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + _operations.Add(b => b.UseServiceProviderFactory(factory)); + return this; + } + + internal void RunDeferredCallbacks(IHostBuilder hostBuilder) + { + foreach (var operation in _operations) + { + operation(hostBuilder); + } + } + } +} diff --git a/src/Core/src/Core/FontsMauiAppBuilderExtensions.cs b/src/Core/src/Core/FontsMauiAppBuilderExtensions.cs new file mode 100644 index 000000000000..3231505d293e --- /dev/null +++ b/src/Core/src/Core/FontsMauiAppBuilderExtensions.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Maui.Hosting; +using Microsoft.Maui.Hosting.Internal; + +namespace Microsoft.Maui +{ + public static class FontsMauiAppBuilderExtensions + { + public static MauiAppBuilder ConfigureFonts(this MauiAppBuilder builder) + { + builder.ConfigureFonts(configureDelegate: null); + return builder; + } + + public static MauiAppBuilder ConfigureFonts(this MauiAppBuilder builder, Action? configureDelegate) + { + builder.Services.TryAddSingleton(svc => new EmbeddedFontLoader(svc.CreateLogger())); + builder.Services.TryAddSingleton(svc => new FontRegistrar(svc.GetRequiredService(), svc.CreateLogger())); + builder.Services.TryAddSingleton(svc => new FontManager(svc.GetRequiredService(), svc.CreateLogger())); + if (configureDelegate != null) + { + builder.Services.AddSingleton(new FontsRegistration(configureDelegate)); + } + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient()); + return builder; + } + + + internal class FontsRegistration + { + private readonly Action _registerFonts; + + public FontsRegistration(Action registerFonts) + { + _registerFonts = registerFonts; + } + + internal void AddFonts(IFontCollection fonts) + { + _registerFonts(fonts); + } + } + + internal class FontInitializer : IMauiInitializeService + { + private readonly IEnumerable _fontsRegistrations; + readonly IFontRegistrar _fontRegistrar; + + public FontInitializer(IEnumerable fontsRegistrations, IFontRegistrar fontRegistrar) + { + _fontsRegistrations = fontsRegistrations; + _fontRegistrar = fontRegistrar; + } + + public void Initialize(IServiceProvider __) + { + if (_fontsRegistrations != null) + { + var fontsBuilder = new FontCollection(); + + // Run all the user-defined registrations + foreach (var font in _fontsRegistrations) + { + font.AddFonts(fontsBuilder); + } + + // Register the fonts in the registrar + foreach (var font in fontsBuilder) + { + if (font.Assembly == null) + _fontRegistrar.Register(font.Filename, font.Alias); + else + _fontRegistrar.Register(font.Filename, font.Alias, font.Assembly); + } + } + } + } + } +} diff --git a/src/Core/src/Core/HandlerMauiAppBuilderExtensions.cs b/src/Core/src/Core/HandlerMauiAppBuilderExtensions.cs new file mode 100644 index 000000000000..f4cdba0e543e --- /dev/null +++ b/src/Core/src/Core/HandlerMauiAppBuilderExtensions.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Maui.Hosting; +using Microsoft.Maui.Hosting.Internal; + +namespace Microsoft.Maui +{ + public static class HandlerMauiAppBuilderExtensions + { + public static MauiAppBuilder ConfigureMauiHandlers(this MauiAppBuilder builder, Action? configureDelegate) + { + builder.Services.TryAddSingleton(); + if (configureDelegate != null) + { + builder.Services.AddSingleton(new HandlerRegistration(configureDelegate)); + } + return builder; + } + + internal class HandlerRegistration + { + private readonly Action _registerAction; + + public HandlerRegistration(Action registerAction) + { + _registerAction = registerAction; + } + + internal void AddRegistration(IMauiHandlersCollection builder) + { + _registerAction(builder); + } + } + } +} diff --git a/src/Core/src/Core/IStartup.cs b/src/Core/src/Core/IStartup.cs deleted file mode 100644 index 44048a803f13..000000000000 --- a/src/Core/src/Core/IStartup.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.Maui.Hosting; - -namespace Microsoft.Maui -{ - /// - /// Startup allow to configure and instantiate application services. - /// - public interface IStartup - { - /// - /// Configures the application. - /// Configure are called by the .NET MAUI Core runtime when the app starts. - /// - /// Defines a class that provides the mechanisms to configure an application's dependencies. - void Configure(IAppHostBuilder appBuilder); - } - - /// - /// Allow to create a custom IAppHostBuilder instance. - /// - public interface IAppHostBuilderStartup : IStartup - { - /// - /// Create and configure a builder object. - /// - /// The new instance of the IAppHostBuilder. - IAppHostBuilder CreateAppHostBuilder(); - } -} \ No newline at end of file diff --git a/src/Core/src/Core/ImageSourcesMauiAppBuilderExtensions.cs b/src/Core/src/Core/ImageSourcesMauiAppBuilderExtensions.cs new file mode 100644 index 000000000000..c9e2dfacc4bd --- /dev/null +++ b/src/Core/src/Core/ImageSourcesMauiAppBuilderExtensions.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Maui.Hosting; +using Microsoft.Maui.Hosting.Internal; + +namespace Microsoft.Maui +{ + public static class ImageSourcesMauiAppBuilderExtensions + { + public static MauiAppBuilder ConfigureImageSources(this MauiAppBuilder builder) + { + builder.ConfigureImageSources(services => + { + services.AddService(svcs => new FileImageSourceService(svcs.GetService(), svcs.CreateLogger())); + services.AddService(svcs => new FontImageSourceService(svcs.GetRequiredService(), svcs.CreateLogger())); + services.AddService(svcs => new StreamImageSourceService(svcs.CreateLogger())); + services.AddService(svcs => new UriImageSourceService(svcs.CreateLogger())); + }); + return builder; + } + + public static MauiAppBuilder ConfigureImageSources(this MauiAppBuilder builder, Action? configureDelegate) + { + if (configureDelegate != null) + { + builder.Services.AddSingleton(new ImageSourceRegistration(configureDelegate)); + } + + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(svcs => new ImageSourceServiceProvider(svcs.GetRequiredService(), svcs)); + builder.Services.TryAddSingleton(); + + return builder; + } + + class ImageSourceRegistration + { + private readonly Action _registerAction; + + public ImageSourceRegistration(Action registerAction) + { + _registerAction = registerAction; + } + + internal void AddRegistration(IImageSourceServiceCollection builder) + { + _registerAction(builder); + } + } + + class ImageSourceServiceBuilder : MauiServiceCollection, IImageSourceServiceCollection + { + public ImageSourceServiceBuilder(IEnumerable registrationActions) + { + if (registrationActions != null) + { + foreach (var effectRegistration in registrationActions) + { + effectRegistration.AddRegistration(this); + } + } + } + } + } +} diff --git a/src/Core/src/Core/MauiApp.cs b/src/Core/src/Core/MauiApp.cs new file mode 100644 index 000000000000..1d96113eb967 --- /dev/null +++ b/src/Core/src/Core/MauiApp.cs @@ -0,0 +1,87 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Maui +{ + public sealed class MauiApp : IHost + { + private readonly IHost _host; + + internal MauiApp(IHost host) + { + _host = host; + Logger = host.Services.GetRequiredService().CreateLogger("TODO: Environment.ApplicationName"); + } + + /// + /// The application's configured services. + /// + public IServiceProvider Services => _host.Services; + + /// + /// The application's configured . + /// + public IConfiguration Configuration => _host.Services.GetRequiredService(); + + /// + /// Allows consumers to be notified of application lifetime events. + /// + public IHostApplicationLifetime Lifetime => _host.Services.GetRequiredService(); + + /// + /// The default logger for the application. + /// + public ILogger Logger { get; } + + /// + /// Initializes a new instance of the class with preconfigured defaults. + /// + /// The . + public static MauiApp Create() => new MauiAppBuilder().Build(); + + /// + /// Initializes a new instance of the class with preconfigured defaults. + /// + /// The . + public static MauiAppBuilder CreateBuilder() => new(); + + /// + /// Initializes a new instance of the class with optional defaults. + /// + /// Whether to create the with common defaults. + /// The . + public static MauiAppBuilder CreateBuilder(bool useDefaults = true) => new(useDefaults); + + /// + /// Start the application. + /// + /// + /// + /// A that represents the startup of the . + /// Successful completion indicates the HTTP server is ready to accept new requests. + /// + public Task StartAsync(CancellationToken cancellationToken = default) => + _host.StartAsync(cancellationToken); + + /// + /// Shuts down the application. + /// + /// + /// + /// A that represents the shutdown of the . + /// Successful completion indicates that all the HTTP server has stopped. + /// + public Task StopAsync(CancellationToken cancellationToken = default) => + _host.StopAsync(cancellationToken); + + /// + /// Disposes the application. + /// + void IDisposable.Dispose() => _host.Dispose(); + } +} diff --git a/src/Core/src/Core/MauiAppBuilder.cs b/src/Core/src/Core/MauiAppBuilder.cs new file mode 100644 index 000000000000..095fc9b7bfcc --- /dev/null +++ b/src/Core/src/Core/MauiAppBuilder.cs @@ -0,0 +1,167 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; + +namespace Microsoft.Maui +{ + /// + /// A builder for .NET MAUI cross-platform applications and services. + /// + public sealed class MauiAppBuilder + { + private readonly HostBuilder _hostBuilder = new(); + private readonly BootstrapHostBuilder _bootstrapHostBuilder; + private readonly MauiApplicationServiceCollection _services = new(); + + private MauiApp? _builtApplication; + + internal MauiAppBuilder(bool useDefaults = true) + { + Services = _services; + + // Run methods to configure both generic and web host defaults early to populate config from appsettings.json + // environment variables (both DOTNET_ and ASPNETCORE_ prefixed) and other possible default sources to prepopulate + // the correct defaults. + _bootstrapHostBuilder = new BootstrapHostBuilder(Services, _hostBuilder.Properties); + + _bootstrapHostBuilder.ConfigureHostConfiguration(config => + { + // Disable reloading config on change so we don't use up system file watchers, which are a limited resource + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-5.0#disable-app-configuration-reload-on-change + config.AddInMemoryCollection(new Dictionary { { "hostBuilder:reloadConfigOnChange", "false" } }); + }); + + // Don't specify the args here since we want to apply them later so that args + // can override the defaults specified by ConfigureWebHostDefaults + _bootstrapHostBuilder.ConfigureDefaults(args: null); + + _bootstrapHostBuilder.ConfigureHostConfiguration(config => + { + // TODO: This has no more code left. Delete? + }); + + Configuration = new(); + + // This is the application configuration + var hostContext = _bootstrapHostBuilder.RunDefaultCallbacks(Configuration, _hostBuilder); + + Logging = new LoggingBuilder(Services); + Host = new ConfigureHostBuilder(hostContext, Configuration, Services); + + if (useDefaults) + { + // Register required services + this.ConfigureMauiHandlers(configureDelegate: null); + + this.ConfigureFonts(); + this.ConfigureImageSources(); + this.ConfigureAnimations(); + this.ConfigureCrossPlatformLifecycleEvents(); + } + } + + /// + /// A collection of services for the application to compose. This is useful for adding user provided or framework provided services. + /// + public IServiceCollection Services { get; } + + /// + /// A collection of configuration providers for the application to compose. This is useful for adding new configuration sources and providers. + /// + public ConfigurationManager Configuration { get; } + + /// + /// A collection of logging providers for the application to compose. This is useful for adding new logging providers. + /// + public ILoggingBuilder Logging { get; } + + /// + /// An for configuring host specific properties, but not building. + /// To build after configuration, call . + /// + public ConfigureHostBuilder Host { get; } + + /// + /// Builds the . + /// + /// A configured . + public MauiApp Build() + { + // Copy the configuration sources into the final IConfigurationBuilder + _hostBuilder.ConfigureHostConfiguration(builder => + { + foreach (var source in ((IConfigurationBuilder)Configuration).Sources) + { + builder.Sources.Add(source); + } + + foreach (var kvp in ((IConfigurationBuilder)Configuration).Properties) + { + builder.Properties[kvp.Key] = kvp.Value; + } + }); + + // This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService). + // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection + _hostBuilder.ConfigureServices((context, services) => + { + // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults + // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen + // until now, so we cannot clear these services even though some are redundant because + // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder. + foreach (var s in _services) + { + services.Add(s); + } + + // Add any services to the user visible service collection so that they are observable + // just in case users capture the Services property. Orchard does this to get a "blueprint" + // of the service collection + + // Drop the reference to the existing collection and set the inner collection + // to the new one. This allows code that has references to the service collection to still function. + _services.InnerCollection = services; + }); + + // Run the other callbacks on the final host builder + Host.RunDeferredCallbacks(_hostBuilder); + + _builtApplication = new MauiApp(_hostBuilder.Build()); + + // Make builder.Configuration match the final configuration. To do that + // we clear the sources and add the built configuration as a source + ((IConfigurationBuilder)Configuration).Sources.Clear(); + Configuration.AddConfiguration(_builtApplication.Configuration); + + // Mark the service collection as read-only to prevent future modifications + _services.IsReadOnly = true; + + + var initServices = _builtApplication.Services.GetServices(); + if (initServices != null) + { + foreach (var instance in initServices) + { + instance.Initialize(_builtApplication.Services); + } + } + + + return _builtApplication; + } + + private class LoggingBuilder : ILoggingBuilder + { + public LoggingBuilder(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } + } + } +} diff --git a/src/Core/src/Core/MauiApplicationServiceCollection.cs b/src/Core/src/Core/MauiApplicationServiceCollection.cs new file mode 100644 index 000000000000..36e1f5303754 --- /dev/null +++ b/src/Core/src/Core/MauiApplicationServiceCollection.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Maui +{ + internal sealed class MauiApplicationServiceCollection : IServiceCollection + { + private IServiceCollection _services = new ServiceCollection(); + + public ServiceDescriptor this[int index] + { + get => _services[index]; + set + { + CheckServicesAccess(); + _services[index] = value; + } + } + public int Count => _services.Count; + + public bool IsReadOnly { get; set; } + + public IServiceCollection InnerCollection + { + get => _services; + set + { + CheckServicesAccess(); + _services = value; + } + } + + public void Add(ServiceDescriptor item) + { + CheckServicesAccess(); + + _services.Add(item); + } + + public void Clear() + { + CheckServicesAccess(); + + _services.Clear(); + } + + public bool Contains(ServiceDescriptor item) + { + return _services.Contains(item); + } + + public void CopyTo(ServiceDescriptor[] array, int arrayIndex) + { + _services.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return _services.GetEnumerator(); + } + + public int IndexOf(ServiceDescriptor item) + { + return _services.IndexOf(item); + } + + public void Insert(int index, ServiceDescriptor item) + { + CheckServicesAccess(); + + _services.Insert(index, item); + } + + public bool Remove(ServiceDescriptor item) + { + CheckServicesAccess(); + + return _services.Remove(item); + } + + public void RemoveAt(int index) + { + CheckServicesAccess(); + + _services.RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private void CheckServicesAccess() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Cannot modify ServiceCollection after application is built."); + } + } + } +} diff --git a/src/Core/src/Hosting/Animations/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/Animations/AppHostBuilderExtensions.cs index 64def495ebe3..4c4bf5eb971b 100644 --- a/src/Core/src/Hosting/Animations/AppHostBuilderExtensions.cs +++ b/src/Core/src/Hosting/Animations/AppHostBuilderExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Maui.Animations; #if __ANDROID__ using Microsoft.Maui.Platform; @@ -8,18 +9,15 @@ namespace Microsoft.Maui.Hosting { public static partial class AppHostBuilderExtensions { - public static IAppHostBuilder ConfigureAnimations(this IAppHostBuilder builder) + public static MauiAppBuilder ConfigureAnimations(this MauiAppBuilder builder) { - builder.ConfigureServices(services => - { #if __ANDROID__ - services.AddSingleton(svcs => new EnergySaverListenerManager()); - services.AddTransient(svcs => new NativeTicker(svcs.GetRequiredService())); + builder.Services.TryAddSingleton(svcs => new EnergySaverListenerManager()); + builder.Services.TryAddTransient(svcs => new NativeTicker(svcs.GetRequiredService())); #else - services.AddTransient(svcs => new NativeTicker()); + builder.Services.TryAddTransient(svcs => new NativeTicker()); #endif - services.AddTransient(svcs => new AnimationManager(svcs.GetRequiredService())); - }); + builder.Services.TryAddTransient(svcs => new AnimationManager(svcs.GetRequiredService())); return builder; } diff --git a/src/Core/src/Hosting/AppHost.cs b/src/Core/src/Hosting/AppHost.cs deleted file mode 100644 index 6dbd530e5b71..000000000000 --- a/src/Core/src/Hosting/AppHost.cs +++ /dev/null @@ -1,21 +0,0 @@ -#nullable enable -using Microsoft.Maui.LifecycleEvents; - -namespace Microsoft.Maui.Hosting -{ - public static class AppHost - { - public static IAppHostBuilder CreateDefaultBuilder() - { - var builder = new AppHostBuilder(); - - builder.UseMicrosoftExtensionsServiceProviderFactory(); - builder.ConfigureFonts(); - builder.ConfigureImageSources(); - builder.ConfigureAnimations(); - builder.ConfigureCrossPlatformLifecycleEvents(); - - return builder; - } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/AppHostBuilder.cs b/src/Core/src/Hosting/AppHostBuilder.cs deleted file mode 100644 index d69537ad6280..000000000000 --- a/src/Core/src/Hosting/AppHostBuilder.cs +++ /dev/null @@ -1,280 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Maui.Hosting.Internal; - -namespace Microsoft.Maui.Hosting -{ - public class AppHostBuilder : IAppHostBuilder - { - readonly Dictionary>> _configureServiceBuilderActions = new Dictionary>>(); - readonly List _configureServiceBuilderInstances = new List(); - readonly List> _configureHostConfigActions = new List>(); - readonly List> _configureAppConfigActions = new List>(); - readonly List> _configureServicesActions = new List>(); - readonly List _configureContainerActions = new List(); - readonly Func _serviceCollectionFactory = new Func(() => new MauiServiceCollection()); - - IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter(new MauiServiceProviderFactory(false)); - - bool _hostBuilt; - HostBuilderContext? _hostBuilderContext; - IHostEnvironment? _hostEnvironment; - IServiceProvider? _serviceProvider; - IServiceCollection? _services; - IConfiguration? _hostConfiguration; - IConfiguration? _appConfiguration; - - public AppHostBuilder() - { - // This is here just to make sure that the IMauiHandlersServiceProvider gets registered. - this.ConfigureMauiHandlers(handlers => { }); - } - - public IDictionary Properties => new Dictionary(); - - public IAppHost Build() - { - if (_hostBuilt) - throw new InvalidOperationException("Build can only be called once."); - - _hostBuilt = true; - - // the order is important here - BuildHostConfiguration(); - CreateHostingEnvironment(); - CreateHostBuilderContext(); - BuildAppConfiguration(); - - _services = _serviceCollectionFactory(); - if (_services == null) - throw new InvalidOperationException("The ServiceCollection cannot be null"); - - BuildServiceCollections(_services); - BuildServices(_services); - - _services.TryAddSingleton(); - - _serviceProvider = ConfigureContainerAndGetProvider(_services); - if (_serviceProvider == null) - throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider."); - - ConfigureServiceCollectionBuilders(_serviceProvider); - - return new Internal.AppHost(_serviceProvider, null); - } - - public IAppHostBuilder ConfigureAppConfiguration(Action configureDelegate) - { - _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); - return this; - } - - public IAppHostBuilder ConfigureContainer(Action configureDelegate) - { - _configureContainerActions.Add(new ConfigureContainerAdapter(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)))); - return this; - } - - public IAppHostBuilder ConfigureHostConfiguration(Action configureDelegate) - { - _configureHostConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); - return this; - } - - public IAppHostBuilder ConfigureServices(Action configureDelegate) - { - _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); - return this; - } - - public IAppHostBuilder ConfigureServices(Action configureDelegate) - where TBuilder : IMauiServiceBuilder, new() - { - _ = configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)); - - var key = typeof(TBuilder); - if (!_configureServiceBuilderActions.TryGetValue(key, out var list)) - { - list = new List>(); - _configureServiceBuilderActions.Add(key, list); - } - - list.Add((context, builder) => configureDelegate(context, (TBuilder)builder)); - - return this; - } - -#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. - public IAppHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) - { - _serviceProviderFactory = new ServiceFactoryAdapter(factory ?? throw new ArgumentNullException(nameof(factory))); - return this; - } - - public IAppHostBuilder UseServiceProviderFactory(Func> factory) - { - _serviceProviderFactory = new ServiceFactoryAdapter(() => _hostBuilderContext!, factory ?? throw new ArgumentNullException(nameof(factory))); - - return this; - } -#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint - - void BuildHostConfiguration() - { - var configBuilder = new ConfigurationBuilder(); - foreach (var buildAction in _configureHostConfigActions) - { - buildAction(configBuilder); - } - _hostConfiguration = configBuilder.Build(); - } - - void CreateHostingEnvironment() - { - _hostEnvironment = new AppHostEnvironment() - { - //ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey], - //EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production, - //ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory), - }; - - if (string.IsNullOrEmpty(_hostEnvironment.ApplicationName)) - { - // Note GetEntryAssembly returns null for the net4x console test runner. - _hostEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name; - } - } - - void CreateHostBuilderContext() - { - _hostBuilderContext = new HostBuilderContext(Properties) - { - HostingEnvironment = _hostEnvironment, - }; - } - - void BuildServices(IServiceCollection services) - { - if (services == null) - throw new ArgumentNullException(nameof(services)); - if (_hostBuilderContext == null) - throw new InvalidOperationException($"The HostBuilderContext was not set."); - - if (_appConfiguration != null) - services.AddSingleton(_appConfiguration); - - foreach (Action configureServicesAction in _configureServicesActions) - { - configureServicesAction(_hostBuilderContext, services); - } - } - - void BuildAppConfiguration() - { - if (_hostBuilderContext == null) - throw new InvalidOperationException($"The HostBuilderContext was not set."); - - var configBuilder = new ConfigurationBuilder(); - configBuilder.AddConfiguration(_hostConfiguration); - foreach (var buildAction in _configureAppConfigActions) - { - buildAction(_hostBuilderContext, configBuilder); - } - _appConfiguration = configBuilder.Build(); - - _hostBuilderContext.Configuration = _appConfiguration; - } - - IServiceProvider ConfigureContainerAndGetProvider(IServiceCollection services) - { - if (_hostBuilderContext == null) - throw new InvalidOperationException($"The HostBuilderContext was not set."); - - object containerBuilder = _serviceProviderFactory.CreateBuilder(services); - - foreach (IConfigureContainerAdapter containerAction in _configureContainerActions) - { - containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder); - } - - return _serviceProviderFactory.CreateServiceProvider(containerBuilder); - } - - void BuildServiceCollections(IServiceCollection? services) - { - if (services == null) - throw new ArgumentNullException(nameof(services)); - if (_hostBuilderContext == null) - throw new InvalidOperationException($"The HostBuilderContext was not set."); - - foreach (var pair in _configureServiceBuilderActions) - { - var instance = (IMauiServiceBuilder)Activator.CreateInstance(pair.Key)!; - - foreach (var action in pair.Value) - { - action(_hostBuilderContext, instance); - } - - instance.ConfigureServices(_hostBuilderContext, services); - - _configureServiceBuilderInstances.Add(instance); - } - } - - void ConfigureServiceCollectionBuilders(IServiceProvider? serviceProvider) - { - if (serviceProvider == null) - throw new ArgumentNullException(nameof(serviceProvider)); - if (_hostBuilderContext == null) - throw new InvalidOperationException($"The HostBuilderContext was not set."); - - foreach (var instance in _configureServiceBuilderInstances) - { - instance.Configure(_hostBuilderContext, serviceProvider); - } - } - - IHostBuilder IHostBuilder.ConfigureHostConfiguration(Action configureDelegate) - { - return ConfigureHostConfiguration(configureDelegate); - } - - IHostBuilder IHostBuilder.ConfigureAppConfiguration(Action configureDelegate) - { - return ConfigureAppConfiguration(configureDelegate); - } - - IHostBuilder IHostBuilder.ConfigureServices(Action configureDelegate) - { - return ConfigureServices(configureDelegate); - } - - IHostBuilder IHostBuilder.UseServiceProviderFactory(IServiceProviderFactory factory) - { - return UseServiceProviderFactory(factory); - } - - IHostBuilder IHostBuilder.UseServiceProviderFactory(Func> factory) - { - return UseServiceProviderFactory(factory); - } - - IHostBuilder IHostBuilder.ConfigureContainer(Action configureDelegate) - { - return ConfigureContainer(configureDelegate); - } - - IHost IHostBuilder.Build() - { - return Build(); - } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/AppHostBuilderExtensions.cs deleted file mode 100644 index 4269fe29bc03..000000000000 --- a/src/Core/src/Hosting/AppHostBuilderExtensions.cs +++ /dev/null @@ -1,90 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Maui.Graphics; -using Microsoft.Maui.Handlers; -using Microsoft.Maui.Hosting.Internal; - -namespace Microsoft.Maui.Hosting -{ - public static partial class AppHostBuilderExtensions - { - public static IAppHostBuilder ConfigureMauiHandlers(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices((_, handlers) => configureDelegate(handlers)); - return builder; - } - - public static IAppHostBuilder ConfigureMauiHandlers(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices(configureDelegate); - return builder; - } - - public static IAppHostBuilder ConfigureServices(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices((_, services) => configureDelegate(services)); - return builder; - } - - public static IAppHostBuilder ConfigureServices(this IAppHostBuilder builder, Action configureDelegate) - where TBuilder : IMauiServiceBuilder, new() - { - builder.ConfigureServices((_, services) => configureDelegate(services)); - return builder; - } - - public static IAppHostBuilder ConfigureServices(this IAppHostBuilder builder) - where TBuilder : IMauiServiceBuilder, new() - { - builder.ConfigureServices((_, services) => { }); - return builder; - } - - public static IAppHostBuilder ConfigureAppConfiguration(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureAppConfiguration((_, config) => configureDelegate(config)); - return builder; - } - - public static IAppHostBuilder UseMauiServiceProviderFactory(this IAppHostBuilder builder, bool constructorInjection) - { - builder.UseServiceProviderFactory(new MauiServiceProviderFactory(constructorInjection)); - return builder; - } - - public static IAppHostBuilder UseMicrosoftExtensionsServiceProviderFactory(this IAppHostBuilder builder) - { - builder.UseServiceProviderFactory(new DIExtensionsServiceProviderFactory()); - return builder; - } - - // To use the Microsoft.Extensions.DependencyInjection ServiceCollection and not the MAUI one - class DIExtensionsServiceProviderFactory : IServiceProviderFactory - { - public ServiceCollection CreateBuilder(IServiceCollection services) - => new ServiceCollection { services }; - - public IServiceProvider CreateServiceProvider(ServiceCollection containerBuilder) - => containerBuilder.BuildServiceProvider(); - } - - class HandlerCollectionBuilder : MauiHandlersCollection, IMauiServiceBuilder - { - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - var provider = new MauiHandlersServiceProvider(this); - - services.AddSingleton(provider); - } - - public void Configure(HostBuilderContext context, IServiceProvider services) - { - } - } - } -} diff --git a/src/Core/src/Hosting/Fonts/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/Fonts/AppHostBuilderExtensions.cs deleted file mode 100644 index 3501ad60ce6e..000000000000 --- a/src/Core/src/Hosting/Fonts/AppHostBuilderExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Maui.Hosting.Internal; - -namespace Microsoft.Maui.Hosting -{ - public static partial class AppHostBuilderExtensions - { - public static IAppHostBuilder ConfigureFonts(this IAppHostBuilder builder) - { - builder.ConfigureServices((a, b) => { }); - return builder; - } - - public static IAppHostBuilder ConfigureFonts(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices((_, fonts) => configureDelegate(fonts)); - return builder; - } - - public static IAppHostBuilder ConfigureFonts(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices(configureDelegate); - return builder; - } - - class FontCollectionBuilder : FontCollection, IMauiServiceBuilder - { - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - services.AddSingleton(svc => new EmbeddedFontLoader(svc.CreateLogger())); - services.AddSingleton(svc => new FontRegistrar(svc.GetRequiredService(), svc.CreateLogger())); - services.AddSingleton(svc => new FontManager(svc.GetRequiredService(), svc.CreateLogger())); - } - - public void Configure(HostBuilderContext context, IServiceProvider services) - { - var fontRegistrar = services.GetService(); - if (fontRegistrar == null) - return; - - foreach (var font in this) - { - if (font.Assembly == null) - fontRegistrar.Register(font.Filename, font.Alias); - else - fontRegistrar.Register(font.Filename, font.Alias, font.Assembly); - } - } - } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/IAppHost.cs b/src/Core/src/Hosting/IAppHost.cs deleted file mode 100644 index ae0ca8e314f6..000000000000 --- a/src/Core/src/Hosting/IAppHost.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace Microsoft.Maui.Hosting -{ - public interface IAppHost : IHost - { - IMauiHandlersServiceProvider Handlers { get; } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/IAppHostBuilder.cs b/src/Core/src/Hosting/IAppHostBuilder.cs deleted file mode 100644 index b2ffa4e82d86..000000000000 --- a/src/Core/src/Hosting/IAppHostBuilder.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.Maui.Hosting -{ - public interface IAppHostBuilder : IHostBuilder - { - IAppHostBuilder ConfigureServices(Action configureDelegate) where TBuilder : IMauiServiceBuilder, new(); - new IAppHostBuilder ConfigureAppConfiguration(Action configureDelegate); - new IAppHostBuilder ConfigureContainer(Action configureDelegate); - new IAppHostBuilder ConfigureHostConfiguration(Action configureDelegate); - new IAppHostBuilder ConfigureServices(Action configureDelegate); -#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. - new IAppHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory); - new IAppHostBuilder UseServiceProviderFactory(Func> factory); -#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. - new IAppHost Build(); - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/IMauiInitializeService.cs b/src/Core/src/Hosting/IMauiInitializeService.cs new file mode 100644 index 000000000000..daecec20ad10 --- /dev/null +++ b/src/Core/src/Hosting/IMauiInitializeService.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.Extensions.Hosting; + +namespace Microsoft.Maui.Hosting +{ + public interface IMauiInitializeService + { + void Initialize(IServiceProvider services); + } +} \ No newline at end of file diff --git a/src/Core/src/Hosting/IMauiServiceBuilder.cs b/src/Core/src/Hosting/IMauiServiceBuilder.cs deleted file mode 100644 index d37c91aab4c5..000000000000 --- a/src/Core/src/Hosting/IMauiServiceBuilder.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.Maui.Hosting -{ - public interface IMauiServiceBuilder - { - void ConfigureServices(HostBuilderContext context, IServiceCollection services); - - void Configure(HostBuilderContext context, IServiceProvider services); - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/ImageSources/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/ImageSources/AppHostBuilderExtensions.cs deleted file mode 100644 index c4ce5f6a854c..000000000000 --- a/src/Core/src/Hosting/ImageSources/AppHostBuilderExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Maui.Hosting.Internal; - -namespace Microsoft.Maui.Hosting -{ - public static partial class AppHostBuilderExtensions - { - public static IAppHostBuilder ConfigureImageSources(this IAppHostBuilder builder) - { - builder.ConfigureImageSources(services => - { - services.AddService(svcs => new FileImageSourceService(svcs.GetService(), svcs.CreateLogger())); - services.AddService(svcs => new FontImageSourceService(svcs.GetRequiredService(), svcs.CreateLogger())); - services.AddService(svcs => new StreamImageSourceService(svcs.CreateLogger())); - services.AddService(svcs => new UriImageSourceService(svcs.CreateLogger())); - }); - return builder; - } - - public static IAppHostBuilder ConfigureImageSources(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices((_, services) => configureDelegate(services)); - return builder; - } - - public static IAppHostBuilder ConfigureImageSources(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices(configureDelegate); - return builder; - } - - class ImageSourceServiceBuilder : MauiServiceCollection, IImageSourceServiceCollection, IMauiServiceBuilder - { - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(svcs => new ImageSourceServiceProvider(this, svcs)); - } - - public void Configure(HostBuilderContext context, IServiceProvider services) - { - } - } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/ImageSources/IImageSourceServiceCollection.cs b/src/Core/src/Hosting/ImageSources/IImageSourceServiceCollection.cs index d1d62e3ebd73..5154f0f98b02 100644 --- a/src/Core/src/Hosting/ImageSources/IImageSourceServiceCollection.cs +++ b/src/Core/src/Hosting/ImageSources/IImageSourceServiceCollection.cs @@ -2,7 +2,7 @@ namespace Microsoft.Maui.Hosting { - public interface IImageSourceServiceCollection : IServiceCollection + public interface IImageSourceServiceCollection : IMauiServiceCollection { } } \ No newline at end of file diff --git a/src/Core/src/Hosting/ImageSources/ImageSourceServiceProvider.cs b/src/Core/src/Hosting/ImageSources/ImageSourceServiceProvider.cs index f872fee3f479..8b5c2bf7c0db 100644 --- a/src/Core/src/Hosting/ImageSources/ImageSourceServiceProvider.cs +++ b/src/Core/src/Hosting/ImageSources/ImageSourceServiceProvider.cs @@ -14,7 +14,7 @@ class ImageSourceServiceProvider : MauiServiceProvider, IImageSourceServiceProvi readonly ConcurrentDictionary _imageSourceCache = new ConcurrentDictionary(); readonly ConcurrentDictionary _serviceCache = new ConcurrentDictionary(); - public ImageSourceServiceProvider(IMauiServiceCollection collection, IServiceProvider hostServiceProvider) + public ImageSourceServiceProvider(IImageSourceServiceCollection collection, IServiceProvider hostServiceProvider) : base(collection, false) { HostServiceProvider = hostServiceProvider; diff --git a/src/Core/src/Hosting/Internal/AppHost.cs b/src/Core/src/Hosting/Internal/AppHost.cs deleted file mode 100644 index 48cdfd352741..000000000000 --- a/src/Core/src/Hosting/Internal/AppHost.cs +++ /dev/null @@ -1,59 +0,0 @@ -#nullable enable -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Maui.Hosting.Internal -{ - class AppHost : IAppHost, IAsyncDisposable - { - readonly ILogger? _logger; - - public AppHost(IServiceProvider services, ILogger? logger) - { - Services = services ?? throw new ArgumentNullException(nameof(services)); - Handlers = Services.GetRequiredService(); - - _logger = logger; - } - - public IServiceProvider Services { get; } - - public IMauiHandlersServiceProvider Handlers { get; } - - public async Task StartAsync(CancellationToken cancellationToken = default) - { - _logger?.Starting(); - - await Task.Run(() => { }); - - _logger?.Started(); - } - - public async Task StopAsync(CancellationToken cancellationToken = default) - { - _logger?.Stopping(); - - _ = await Task.FromResult(new object()); - - _logger?.Stopped(); - } - - public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult(); - - public async ValueTask DisposeAsync() - { - switch (Services) - { - case IAsyncDisposable asyncDisposable: - await asyncDisposable.DisposeAsync().ConfigureAwait(false); - break; - case IDisposable disposable: - disposable.Dispose(); - break; - } - } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/Internal/AppHostEnvironment.cs b/src/Core/src/Hosting/Internal/AppHostEnvironment.cs deleted file mode 100644 index a25dd97558f6..000000000000 --- a/src/Core/src/Hosting/Internal/AppHostEnvironment.cs +++ /dev/null @@ -1,18 +0,0 @@ -#nullable enable -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.Maui.Hosting.Internal -{ - class AppHostEnvironment : IHostEnvironment - { - public AppHostEnvironment() - { - } - - public string? EnvironmentName { get; set; } - public string? ApplicationName { get; set; } - public string? ContentRootPath { get; set; } - public IFileProvider? ContentRootFileProvider { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/src/Hosting/Internal/MauiHandlersServiceProvider.cs b/src/Core/src/Hosting/Internal/MauiHandlersServiceProvider.cs index ef9e01c41ff3..17dabc99837c 100644 --- a/src/Core/src/Hosting/Internal/MauiHandlersServiceProvider.cs +++ b/src/Core/src/Hosting/Internal/MauiHandlersServiceProvider.cs @@ -1,16 +1,27 @@ #nullable enable using System; +using System.Collections.Generic; namespace Microsoft.Maui.Hosting.Internal { class MauiHandlersServiceProvider : MauiServiceProvider, IMauiHandlersServiceProvider { - readonly IMauiHandlersCollection _collection; + public MauiHandlersServiceProvider(IEnumerable registrationActions) : + base(CreateHandlerCollection(registrationActions), constructorInjection: false) + { + } - public MauiHandlersServiceProvider(IMauiHandlersCollection collection) - : base(collection, false) + private static IMauiServiceCollection CreateHandlerCollection(IEnumerable registrationActions) { - _collection = collection; + var collection = new MauiHandlersCollection(); + if (registrationActions != null) + { + foreach (var registrationAction in registrationActions) + { + registrationAction.AddRegistration(collection); + } + } + return collection; } public IElementHandler? GetHandler(Type type) @@ -28,6 +39,6 @@ public MauiHandlersServiceProvider(IMauiHandlersCollection collection) return null; } - public IMauiHandlersCollection GetCollection() => _collection; + public IMauiHandlersCollection GetCollection() => (IMauiHandlersCollection)InternalCollection; } } \ No newline at end of file diff --git a/src/Core/src/Hosting/Internal/MauiServiceProvider.cs b/src/Core/src/Hosting/Internal/MauiServiceProvider.cs index da04ad2979f6..4490bf7b5aa0 100644 --- a/src/Core/src/Hosting/Internal/MauiServiceProvider.cs +++ b/src/Core/src/Hosting/Internal/MauiServiceProvider.cs @@ -17,6 +17,8 @@ class MauiServiceProvider : IMauiServiceProvider readonly IMauiServiceCollection _collection; readonly bool _constructorInjection; + protected IMauiServiceCollection InternalCollection => _collection; + // TODO: do this properly and support scopes readonly IDictionary _singletons; diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs index ba2aa30f19b4..5f2da9c33f6e 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.LifecycleEvents { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCrossPlatformLifecycleEvents(this MauiAppBuilder builder) => builder.ConfigureLifecycleEvents(events => events.AddAndroid(OnConfigureLifeCycle)); static void OnConfigureLifeCycle(IAndroidLifecycleBuilder android) diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs index cd880a07c910..ae21e1cae5ee 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.LifecycleEvents { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCrossPlatformLifecycleEvents(this MauiAppBuilder builder) => builder; } } diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs index 030f1ecd20de..56a51980422e 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.LifecycleEvents { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCrossPlatformLifecycleEvents(this MauiAppBuilder builder) => builder.ConfigureLifecycleEvents(events => events.AddWindows(OnConfigureLifeCycle)); static void OnConfigureLifeCycle(IWindowsLifecycleBuilder windows) diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.cs index 3370e0b0496a..4c08da0e3656 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.cs @@ -1,36 +1,35 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Maui.Hosting; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace Microsoft.Maui.LifecycleEvents { - public static partial class AppHostBuilderExtensions + public class LifecycleEventRegistration { - public static IAppHostBuilder ConfigureLifecycleEvents(this IAppHostBuilder builder, Action configureDelegate) - { - builder.ConfigureServices((_, lifecycle) => configureDelegate(lifecycle)); + private readonly Action _registerAction; - return builder; + public LifecycleEventRegistration(Action registerAction) + { + _registerAction = registerAction; } - public static IAppHostBuilder ConfigureLifecycleEvents(this IAppHostBuilder builder, Action configureDelegate) + internal void AddRegistration(ILifecycleBuilder effects) { - builder.ConfigureServices(configureDelegate); - - return builder; + _registerAction(effects); } + } - class LifecycleBuilder : LifecycleEventService, ILifecycleBuilder, IMauiServiceBuilder + public static partial class MauiAppHostBuilderExtensions + { + public static MauiAppBuilder ConfigureLifecycleEvents(this MauiAppBuilder builder, Action? configureDelegate) { - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) + builder.Services.TryAddSingleton(); + if (configureDelegate != null) { - services.AddSingleton(this); + builder.Services.AddSingleton(new LifecycleEventRegistration(configureDelegate)); } - public void Configure(HostBuilderContext context, IServiceProvider services) - { - } + return builder; } } } \ No newline at end of file diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs index 4ee68411fe1c..f8ee914c8ccf 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.LifecycleEvents { public static partial class AppHostBuilderExtensions { - internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + internal static MauiAppBuilder ConfigureCrossPlatformLifecycleEvents(this MauiAppBuilder builder) => builder.ConfigureLifecycleEvents(events => events.AddiOS(OnConfigureLifeCycle)); static void OnConfigureLifeCycle(IiOSLifecycleBuilder iOS) diff --git a/src/Core/src/Hosting/StartupExtensions.cs b/src/Core/src/Hosting/StartupExtensions.cs deleted file mode 100644 index d4b70a669bca..000000000000 --- a/src/Core/src/Hosting/StartupExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Microsoft.Maui.Hosting -{ - public static class StartupExtensions - { - public static IAppHostBuilder CreateAppHostBuilder(this IStartup startup) - { - if (startup is IAppHostBuilderStartup hostBuilderStartup) - return hostBuilderStartup.CreateAppHostBuilder(); - - return AppHost.CreateDefaultBuilder(); - } - - public static IAppHostBuilder ConfigureUsing(this IAppHostBuilder appHostBuilder, IStartup startup) - { - startup.Configure(appHostBuilder); - - return appHostBuilder; - } - } -} \ No newline at end of file diff --git a/src/Core/src/HotReload/AppHostBuilderExtensions.cs b/src/Core/src/HotReload/AppHostBuilderExtensions.cs index 7c1d786964ff..b2e6e351e264 100644 --- a/src/Core/src/HotReload/AppHostBuilderExtensions.cs +++ b/src/Core/src/HotReload/AppHostBuilderExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Maui.HotReload; @@ -9,27 +10,23 @@ namespace Microsoft.Maui.Hosting { public static partial class AppHostBuilderExtensions { - public static IAppHostBuilder EnableHotReload(this IAppHostBuilder builder, string? ideIp = null, int idePort = 9988) + public static MauiAppBuilder EnableHotReload(this MauiAppBuilder builder, string? ideIp = null, int idePort = 9988) { - builder.ConfigureServices(hotReload => + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient(_ => new HotReloadBuilder { - hotReload.IdeIp = ideIp; - hotReload.IdePort = idePort; - }); + IdeIp = ideIp, + IdePort = idePort, + })); return builder; } - class HotReloadBuilder : IMauiServiceBuilder + class HotReloadBuilder : IMauiInitializeService { public string? IdeIp { get; set; } public int IdePort { get; set; } = 9988; - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - } - - public async void Configure(HostBuilderContext context, IServiceProvider services) + public async void Initialize(IServiceProvider services) { var handlers = services.GetRequiredService(); diff --git a/src/Core/src/LifecycleEvents/LifecycleEventService.cs b/src/Core/src/LifecycleEvents/LifecycleEventService.cs index 9b5ba9560a42..40c2d0d07879 100644 --- a/src/Core/src/LifecycleEvents/LifecycleEventService.cs +++ b/src/Core/src/LifecycleEvents/LifecycleEventService.cs @@ -4,10 +4,21 @@ namespace Microsoft.Maui.LifecycleEvents { - public class LifecycleEventService : ILifecycleEventService + public class LifecycleEventService : ILifecycleEventService, ILifecycleBuilder { readonly Dictionary> _mapper = new Dictionary>(); + public LifecycleEventService(IEnumerable registrations) + { + if (registrations != null) + { + foreach (var registrationAction in registrations) + { + registrationAction.AddRegistration(this); + } + } + } + public void AddEvent(string eventName, TDelegate action) where TDelegate : Delegate { diff --git a/src/Core/src/Platform/Android/MauiApplication.cs b/src/Core/src/Platform/Android/MauiApplication.cs index c86bed7fd08a..d62f1e313bc0 100644 --- a/src/Core/src/Platform/Android/MauiApplication.cs +++ b/src/Core/src/Platform/Android/MauiApplication.cs @@ -11,16 +11,6 @@ namespace Microsoft.Maui { - public class MauiApplication : MauiApplication - where TStartup : IStartup, new() - { - public MauiApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) - { - } - - protected override IStartup OnCreateStartup() => new TStartup(); - } - public abstract class MauiApplication : Application { protected MauiApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) @@ -28,21 +18,15 @@ protected MauiApplication(IntPtr handle, JniHandleOwnership ownership) : base(ha Current = this; } - protected abstract IStartup OnCreateStartup(); + protected abstract MauiApp CreateMauiApp(); public override void OnCreate() { RegisterActivityLifecycleCallbacks(new ActivityLifecycleCallbacks()); - var startup = OnCreateStartup(); - - var host = startup - .CreateAppHostBuilder() - .ConfigureServices(ConfigureNativeServices) - .ConfigureUsing(startup) - .Build(); + var mauiApp = CreateMauiApp(); - Services = host.Services; + Services = mauiApp.Services; Current.Services?.InvokeLifecycleEvents(del => del(this)); @@ -74,11 +58,6 @@ public override void OnConfigurationChanged(Configuration newConfig) base.OnConfigurationChanged(newConfig); } - // Configure native services like HandlersContext, ImageSourceHandlers etc.. - void ConfigureNativeServices(HostBuilderContext ctx, IServiceCollection services) - { - } - public static MauiApplication Current { get; private set; } = null!; public IServiceProvider Services { get; protected set; } = null!; diff --git a/src/Core/src/Platform/Windows/MauiWinUIApplication.cs b/src/Core/src/Platform/Windows/MauiWinUIApplication.cs index 0bcdf5484b0a..41d5f4b24eea 100644 --- a/src/Core/src/Platform/Windows/MauiWinUIApplication.cs +++ b/src/Core/src/Platform/Windows/MauiWinUIApplication.cs @@ -6,30 +6,17 @@ namespace Microsoft.Maui { - public class MauiWinUIApplication : MauiWinUIApplication - where TStartup : IStartup, new() - { - protected override IStartup OnCreateStartup() => new TStartup(); - } - public abstract class MauiWinUIApplication : UI.Xaml.Application { - protected abstract IStartup OnCreateStartup(); + protected abstract MauiApp CreateMauiApp(); protected override void OnLaunched(UI.Xaml.LaunchActivatedEventArgs args) { LaunchActivatedEventArgs = args; - var startup = OnCreateStartup() ?? - throw new InvalidOperationException($"A valid startup object must be provided by overriding {nameof(OnCreateStartup)}."); - - var host = startup - .CreateAppHostBuilder() - .ConfigureServices(ConfigureNativeServices) - .ConfigureUsing(startup) - .Build(); + var mauiApp = CreateMauiApp(); - Services = host.Services; + Services = mauiApp.Services; Services.InvokeLifecycleEvents(del => del(this, args)); @@ -61,10 +48,6 @@ UI.Xaml.Window CreateNativeWindow(UI.Xaml.LaunchActivatedEventArgs? args = null) return winuiWndow; } - void ConfigureNativeServices(HostBuilderContext ctx, IServiceCollection services) - { - } - public static new MauiWinUIApplication Current => (MauiWinUIApplication)UI.Xaml.Application.Current; public UI.Xaml.LaunchActivatedEventArgs LaunchActivatedEventArgs { get; protected set; } = null!; diff --git a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs index 3bc9fc493059..d9a9e0b4a72c 100644 --- a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs @@ -8,12 +8,6 @@ namespace Microsoft.Maui { - public class MauiUIApplicationDelegate : MauiUIApplicationDelegate - where TStartup : IStartup, new() - { - protected override IStartup OnCreateStartup() => new TStartup(); - } - public abstract class MauiUIApplicationDelegate : UIApplicationDelegate, IUIApplicationDelegate { WeakReference? _virtualWindow; @@ -32,19 +26,13 @@ protected MauiUIApplicationDelegate() Current = this; } - protected abstract IStartup OnCreateStartup(); + protected abstract MauiApp CreateMauiApp(); public override bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) { - var startup = OnCreateStartup(); - - var host = startup - .CreateAppHostBuilder() - .ConfigureServices(ConfigureNativeServices) - .ConfigureUsing(startup) - .Build(); + var mauiApp = CreateMauiApp(); - Services = host.Services; + Services = mauiApp.Services; Current.Services?.InvokeLifecycleEvents(del => del(application, launchOptions)); @@ -136,11 +124,6 @@ public override void WillEnterForeground(UIApplication application) Current.Services?.InvokeLifecycleEvents(del => del(application)); } - // Configure native services like HandlersContext, ImageSourceHandlers etc.. - void ConfigureNativeServices(HostBuilderContext ctx, IServiceCollection services) - { - } - public static MauiUIApplicationDelegate Current { get; private set; } = null!; public override UIWindow? Window { get; set; } diff --git a/src/Core/tests/Benchmarks/Benchmarks/GetHandlersBenchmarker.cs b/src/Core/tests/Benchmarks/Benchmarks/GetHandlersBenchmarker.cs index df647c3d5396..183982d5fb3a 100644 --- a/src/Core/tests/Benchmarks/Benchmarks/GetHandlersBenchmarker.cs +++ b/src/Core/tests/Benchmarks/Benchmarks/GetHandlersBenchmarker.cs @@ -1,4 +1,5 @@ using BenchmarkDotNet.Attributes; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Hosting; namespace Microsoft.Maui.Handlers.Benchmarks @@ -6,7 +7,7 @@ namespace Microsoft.Maui.Handlers.Benchmarks [MemoryDiagnoser] public class GetHandlersBenchmarker { - IAppHost _host; + MauiApp _mauiApp; Registrar _registrar; @@ -16,8 +17,8 @@ public class GetHandlersBenchmarker [GlobalSetup(Target = nameof(GetHandlerUsingDI))] public void SetupForDI() { - _host = AppHost - .CreateDefaultBuilder() + _mauiApp = MauiApp + .CreateBuilder() .Build(); } @@ -31,9 +32,10 @@ public void SetupForRegistrar() [Benchmark] public void GetHandlerUsingDI() { + var handlers = _mauiApp.Services.GetRequiredService(); for (int i = 0; i < N; i++) { - _host.Handlers.GetHandler(); + handlers.GetHandler(); } } diff --git a/src/Core/tests/Benchmarks/Benchmarks/MauiServiceProviderBenchmarker.cs b/src/Core/tests/Benchmarks/Benchmarks/MauiServiceProviderBenchmarker.cs index 7dbcb582ca9f..4c35c4c475b5 100644 --- a/src/Core/tests/Benchmarks/Benchmarks/MauiServiceProviderBenchmarker.cs +++ b/src/Core/tests/Benchmarks/Benchmarks/MauiServiceProviderBenchmarker.cs @@ -2,14 +2,13 @@ using BenchmarkDotNet.Attributes; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Maui.Hosting; namespace Microsoft.Maui.Handlers.Benchmarks { [MemoryDiagnoser] public class MauiServiceProviderBenchmarker { - IAppHost _host; + IServiceProvider _serviceProvider; [Params(100_000)] public int N { get; set; } @@ -17,88 +16,84 @@ public class MauiServiceProviderBenchmarker [IterationSetup(Target = nameof(DefaultBuilder))] public void SetupForDefaultBuilder() { - _host = AppHost - .CreateDefaultBuilder() - .ConfigureServices((ctx, svc) => svc.AddTransient()) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [IterationSetup(Target = nameof(DefaultBuilderWithConstructorInjection))] public void SetupForDefaultBuilderWithConstructorInjection() { - _host = AppHost - .CreateDefaultBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, svc) => svc.AddTransient()) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [IterationSetup(Target = nameof(OneConstructorParameter))] public void SetupForOneConstructorParameter() { - _host = AppHost - .CreateDefaultBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, svc) => - { - svc.AddTransient(); - svc.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [IterationSetup(Target = nameof(TwoConstructorParameters))] public void SetupForTwoConstructorParameters() { - _host = AppHost - .CreateDefaultBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, svc) => - { - svc.AddTransient(); - svc.AddTransient(); - svc.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [IterationSetup(Target = nameof(ExtensionsWithConstructorInjection))] public void SetupForExtensionsWithConstructorInjection() { - _host = AppHost - .CreateDefaultBuilder() - .UseServiceProviderFactory(new DIExtensionsServiceProviderFactory()) - .ConfigureServices((ctx, svc) => svc.AddTransient()) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [IterationSetup(Target = nameof(ExtensionsWithOneConstructorParameter))] public void SetupForExtensionsWithOneConstructorParameter() { - _host = AppHost - .CreateDefaultBuilder() - .UseServiceProviderFactory(new DIExtensionsServiceProviderFactory()) - .ConfigureServices((ctx, svc) => - { - svc.AddTransient(); - svc.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [IterationSetup(Target = nameof(ExtensionsWithTwoConstructorParameters))] public void SetupForExtensionsWithTwoConstructorParameters() { - _host = AppHost - .CreateDefaultBuilder() - .UseServiceProviderFactory(new DIExtensionsServiceProviderFactory()) - .ConfigureServices((ctx, svc) => - { - svc.AddTransient(); - svc.AddTransient(); - svc.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + var mauiApp = builder.Build(); + _serviceProvider = mauiApp.Services; } [Benchmark(Baseline = true)] @@ -106,7 +101,7 @@ public void DefaultBuilder() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } @@ -115,7 +110,7 @@ public void DefaultBuilderWithConstructorInjection() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } @@ -124,7 +119,7 @@ public void OneConstructorParameter() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } @@ -133,7 +128,7 @@ public void TwoConstructorParameters() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } @@ -142,7 +137,7 @@ public void ExtensionsWithConstructorInjection() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } @@ -151,7 +146,7 @@ public void ExtensionsWithOneConstructorParameter() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } @@ -160,7 +155,7 @@ public void ExtensionsWithTwoConstructorParameters() { for (int i = 0; i < N; i++) { - _host.Services.GetService(); + _serviceProvider.GetService(); } } diff --git a/src/Core/tests/Benchmarks/Benchmarks/RegisterHandlersBenchmarker.cs b/src/Core/tests/Benchmarks/Benchmarks/RegisterHandlersBenchmarker.cs index 90e4a3671240..933909eb99bb 100644 --- a/src/Core/tests/Benchmarks/Benchmarks/RegisterHandlersBenchmarker.cs +++ b/src/Core/tests/Benchmarks/Benchmarks/RegisterHandlersBenchmarker.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.Handlers.Benchmarks [MemoryDiagnoser] public class RegisterHandlersBenchmarker { - IAppHostBuilder _builder; + MauiAppBuilder _builder; Registrar _registrar; @@ -16,7 +16,7 @@ public class RegisterHandlersBenchmarker [IterationSetup(Target = nameof(RegisterHandlerUsingDI))] public void SetupForDI() { - _builder = new AppHostBuilder(); + _builder = MauiApp.CreateBuilder(); } [IterationSetup(Target = nameof(RegisterHandlerUsingRegistrar))] @@ -30,7 +30,7 @@ public void RegisterHandlerUsingDI() { for (int i = 0; i < N; i++) { - _builder.ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()); + _builder.ConfigureMauiHandlers(handlers => handlers.AddHandler()); } } diff --git a/src/Core/tests/DeviceTests/Handlers/HandlerTestBase.cs b/src/Core/tests/DeviceTests/Handlers/HandlerTestBase.cs index dd18e7ce42b5..88a86ca02bfe 100644 --- a/src/Core/tests/DeviceTests/Handlers/HandlerTestBase.cs +++ b/src/Core/tests/DeviceTests/Handlers/HandlerTestBase.cs @@ -13,24 +13,25 @@ public partial class HandlerTestBase : TestBase, IDisposable where TStub : StubBase, IView, new() { IApplication _app; - IAppHost _host; + MauiApp _mauiApp; + IServiceProvider _servicesProvider; IMauiContext _context; static readonly Random rnd = new Random(); public HandlerTestBase() { - var appBuilder = AppHost - .CreateDefaultBuilder() + var appBuilder = MauiApp + .CreateBuilder() .ConfigureMauiHandlers(handlers => { handlers.AddHandler(typeof(SliderStub), typeof(SliderHandler)); handlers.AddHandler(typeof(ButtonStub), typeof(ButtonHandler)); }) - .ConfigureImageSources((ctx, services) => + .ConfigureImageSources(services => { services.AddService(); }) - .ConfigureFonts((ctx, fonts) => + .ConfigureFonts(fonts => { fonts.AddFont("dokdo_regular.ttf", "Dokdo"); fonts.AddFont("LobsterTwo-Regular.ttf", "Lobster Two"); @@ -39,11 +40,12 @@ public HandlerTestBase() fonts.AddFont("LobsterTwo-BoldItalic.ttf", "Lobster Two BoldItalic"); }); - _host = appBuilder.Build(); + _mauiApp = appBuilder.Build(); + _servicesProvider = _mauiApp.Services; _app = new ApplicationStub(); - _context = new ContextStub(_host.Services); + _context = new ContextStub(_servicesProvider); } public static async Task Wait(Func exitCondition, int timeout = 1000) @@ -61,8 +63,9 @@ public static async Task Wait(Func exitCondition, int timeout = 1000 public void Dispose() { - _host.Dispose(); - _host = null; + ((IDisposable)_mauiApp).Dispose(); + _mauiApp = null; + _servicesProvider = null; _app = null; _context = null; } diff --git a/src/Core/tests/DeviceTests/Platforms/Windows/App.xaml.cs b/src/Core/tests/DeviceTests/Platforms/Windows/App.xaml.cs index f27ac349885d..6807c71988d0 100644 --- a/src/Core/tests/DeviceTests/Platforms/Windows/App.xaml.cs +++ b/src/Core/tests/DeviceTests/Platforms/Windows/App.xaml.cs @@ -22,7 +22,8 @@ public App() } } - public class MiddleApp : MauiWinUIApplication + public class MiddleApp : MauiWinUIApplication { - } + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } } diff --git a/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.Android.cs b/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.Android.cs index aac35e3d7325..3995999ec893 100644 --- a/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.Android.cs +++ b/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.Android.cs @@ -32,12 +32,12 @@ public async Task GetDrawableAsync(string colorHex) { var expectedColor = Color.FromArgb(colorHex); - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .ConfigureFonts() .ConfigureImageSources() .Build(); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); var service = images.GetRequiredImageSourceService(); var imageSource = new FontImageSourceStub @@ -59,7 +59,7 @@ public async Task GetDrawableAsync(string colorHex) [Fact] public async Task GetDrawableAsyncWithCustomFont() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .ConfigureFonts(fonts => { fonts.AddFont("dokdo_regular.ttf", "Dokdo"); @@ -67,7 +67,7 @@ public async Task GetDrawableAsyncWithCustomFont() .ConfigureImageSources() .Build(); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); var service = images.GetRequiredImageSourceService(); var imageSource = new FontImageSourceStub diff --git a/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.iOS.cs b/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.iOS.cs index 0112ab6897e1..6d3d50ead35f 100644 --- a/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Services/ImageSource/FontImageSourceServiceTests.iOS.cs @@ -32,12 +32,12 @@ public async Task GetImageAsync(string colorHex) { var expectedColor = Color.FromArgb(colorHex); - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .ConfigureFonts() .ConfigureImageSources() .Build(); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); var service = images.GetRequiredImageSourceService(); var imageSource = new FontImageSourceStub @@ -57,7 +57,7 @@ public async Task GetImageAsync(string colorHex) [Fact] public async Task GetImageAsyncWithCustomFont() { - var host = new AppHostBuilder() + var mauiApp = MauiApp.CreateBuilder() .ConfigureFonts(fonts => { fonts.AddFont("dokdo_regular.ttf", "Dokdo"); @@ -65,7 +65,7 @@ public async Task GetImageAsyncWithCustomFont() .ConfigureImageSources() .Build(); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); var service = images.GetRequiredImageSourceService(); var imageSource = new FontImageSourceStub diff --git a/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs b/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs index 89147fa86a6a..34e4980f195c 100644 --- a/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs +++ b/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs @@ -89,14 +89,11 @@ public void ResultsDisposeCorrectlyAndOnce() private IImageSourceServiceProvider CreateImageSourceServiceProvider(Action configure) { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) + var mauiApp = MauiApp.CreateBuilder(useDefaults: false) .ConfigureImageSources(configure) .Build(); - var services = host.Services; - - var provider = services.GetRequiredService(); + var provider = mauiApp.Services.GetRequiredService(); return provider; } diff --git a/src/Core/tests/DeviceTests/Startup.cs b/src/Core/tests/DeviceTests/Startup.cs index 439d62535382..02a92e83a064 100644 --- a/src/Core/tests/DeviceTests/Startup.cs +++ b/src/Core/tests/DeviceTests/Startup.cs @@ -4,10 +4,11 @@ namespace Microsoft.Maui.DeviceTests { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { + var appBuilder = MauiApp.CreateBuilder(); appBuilder .ConfigureLifecycleEvents(life => { @@ -22,7 +23,7 @@ public void Configure(IAppHostBuilder appBuilder) { Assemblies = { - typeof(Startup).Assembly + typeof(MauiProgram).Assembly }, }) .UseHeadlessRunner(new HeadlessRunnerOptions @@ -30,6 +31,8 @@ public void Configure(IAppHostBuilder appBuilder) RequiresUIContext = true, }) .UseVisualRunner(); + + return appBuilder.Build(); } } } \ No newline at end of file diff --git a/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs b/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs index c05ebb46e823..9495c400af14 100644 --- a/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs +++ b/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs @@ -76,7 +76,7 @@ public void GetRequiredServiceRetrievesService() HandlerStub handlerStub = new HandlerStub(); var collection = new MauiServiceCollection(); - collection.TryAddSingleton(new MauiHandlersServiceProvider(new MauiHandlersCollection())); + collection.TryAddSingleton(new MauiHandlersServiceProvider(null)); collection.TryAddSingleton(); var provider = new MauiServiceProvider(collection, false); diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderAppConfigurationTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderAppConfigurationTests.cs index b0f41086aa62..5e036422ec5c 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderAppConfigurationTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderAppConfigurationTests.cs @@ -12,17 +12,18 @@ public class HostBuilderAppConfigurationTests [Fact] public void ConfigureAppConfigurationConfiguresValues() { - var host = new AppHostBuilder() + var builder = MauiApp.CreateBuilder(); + builder.Host .ConfigureAppConfiguration((_, builder) => { builder.AddInMemoryCollection(new Dictionary { { "key 1", "value 1" }, }); - }) - .Build(); + }); + var mauiApp = builder.Build(); - var configuration = host.Services.GetRequiredService(); + var configuration = mauiApp.Services.GetRequiredService(); Assert.Equal("value 1", configuration["key 1"]); } @@ -30,7 +31,8 @@ public void ConfigureAppConfigurationConfiguresValues() [Fact] public void ConfigureAppConfigurationOverwritesValues() { - var host = new AppHostBuilder() + var builder = MauiApp.CreateBuilder(); + builder.Host .ConfigureAppConfiguration((_, builder) => { builder.AddInMemoryCollection(new Dictionary @@ -45,10 +47,10 @@ public void ConfigureAppConfigurationOverwritesValues() { { "key 1", "value a" }, }); - }) - .Build(); + }); + var mauiApp = builder.Build(); - var configuration = host.Services.GetRequiredService(); + var configuration = mauiApp.Services.GetRequiredService(); Assert.Equal("value a", configuration["key 1"]); Assert.Equal("value 2", configuration["key 2"]); @@ -59,19 +61,24 @@ public void ConfigureServicesCanUseConfig() { string value = null; - var host = new AppHostBuilder() + var builder = MauiApp.CreateBuilder(); + builder.Host .ConfigureAppConfiguration((_, builder) => { builder.AddInMemoryCollection(new Dictionary { { "key 1", "value 1" }, }); - }) + }); + + // TODO: CHECK THIS WORKS! + + builder.Host .ConfigureServices((context, services) => { value = context.Configuration["key 1"]; - }) - .Build(); + }); + var mauiApp = builder.Build(); Assert.Equal("value 1", value); } diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs index 1ab01e5f13c5..60581c6832f9 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Maui.Graphics; using Microsoft.Maui.Hosting; using Xunit; @@ -12,17 +13,18 @@ public class HostBuilderFontsTests [Fact] public void ConfigureFontsRegistersTheCorrectServices() { - var host = new AppHostBuilder() - .ConfigureFonts() - .Build(); + var builder = MauiApp + .CreateBuilder() + .ConfigureFonts(); + var mauiApp = builder.Build(); - var manager = host.Services.GetRequiredService(); + var manager = mauiApp.Services.GetRequiredService(); Assert.NotNull(manager); - var registrar = host.Services.GetRequiredService(); + var registrar = mauiApp.Services.GetRequiredService(); Assert.NotNull(registrar); - var loader = host.Services.GetRequiredService(); + var loader = mauiApp.Services.GetRequiredService(); Assert.NotNull(loader); } @@ -33,12 +35,13 @@ public void ConfigureFontsRegistersFonts(string filename, string alias) { var root = Path.Combine(Path.GetTempPath(), "Microsoft.Maui.UnitTests", "ConfigureFontsRegistersFonts", Guid.NewGuid().ToString()); - var host = new AppHostBuilder() - .ConfigureFonts((_, fonts) => fonts.AddEmeddedResourceFont(GetType().Assembly, filename, alias)) - .ConfigureServices((_, services) => services.AddSingleton(_ => new FileSystemEmbeddedFontLoader(root))) - .Build(); + var builder = MauiApp + .CreateBuilder() + .ConfigureFonts(fonts => fonts.AddEmeddedResourceFont(GetType().Assembly, filename, alias)); + builder.Services.AddSingleton(_ => new FileSystemEmbeddedFontLoader(root)); + var mauiApp = builder.Build(); - var registrar = host.Services.GetRequiredService(); + var registrar = mauiApp.Services.GetRequiredService(); var path = registrar.GetFont(filename); Assert.NotNull(path); @@ -59,8 +62,9 @@ public void ConfigureFontsRegistersFonts(string filename, string alias) [Fact] public void NullAssemblyForEmbeddedFontThrows() { - var builder = new AppHostBuilder() - .ConfigureFonts((_, fonts) => fonts.AddEmeddedResourceFont(null, "test.ttf")); + var builder = MauiApp + .CreateBuilder() + .ConfigureFonts(fonts => fonts.AddEmeddedResourceFont(null, "test.ttf")); var ex = Assert.Throws(() => builder.Build()); Assert.Equal("assembly", ex.ParamName); @@ -72,8 +76,9 @@ public void NullAssemblyForEmbeddedFontThrows() [InlineData(" ")] public void BadFileNameForEmbeddedFontThrows(string filename) { - var builder = new AppHostBuilder() - .ConfigureFonts((_, fonts) => fonts.AddEmeddedResourceFont(GetType().Assembly, filename)); + var builder = MauiApp + .CreateBuilder() + .ConfigureFonts(fonts => fonts.AddEmeddedResourceFont(GetType().Assembly, filename)); var ex = Assert.ThrowsAny(() => builder.Build()); Assert.Equal("filename", ex.ParamName); @@ -85,9 +90,11 @@ public void BadFileNameForEmbeddedFontThrows(string filename) [Fact] public void NullAliasForEmbeddedFontDoesNotThrow() { - new AppHostBuilder() - .ConfigureFonts((_, fonts) => fonts.AddEmeddedResourceFont(GetType().Assembly, "test.ttf", null)) - .Build(); + var builder = MauiApp + .CreateBuilder() + .ConfigureFonts(fonts => fonts.AddEmeddedResourceFont(GetType().Assembly, "test.ttf", null)); + + _ = builder.Build(); } } } \ No newline at end of file diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs index c2489bf4f0d9..5b5578170c22 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Xunit; @@ -13,36 +12,32 @@ public class HostBuilderHandlerTests [Fact] public void CanBuildAHost() { - var host = AppHost - .CreateDefaultBuilder() + var mauiApp = MauiApp.CreateBuilder() .Build(); - Assert.NotNull(host); + Assert.NotNull(mauiApp.Services); } [Fact] public void CanGetIMauiHandlersServiceProviderFromServices() { - var host = AppHost - .CreateDefaultBuilder() + var mauiApp = MauiApp.CreateBuilder() .Build(); - Assert.NotNull(host); - Assert.NotNull(host.Services); - Assert.NotNull(host.Handlers); - Assert.IsType(host.Handlers); - Assert.Equal(host.Handlers, host.Services.GetService()); + Assert.NotNull(mauiApp.Services); + var handlers = mauiApp.Services.GetRequiredService(); + Assert.NotNull(handlers); + Assert.IsType(handlers); } [Fact] public void CanRegisterAndGetHandlerUsingType() { - var host = AppHost - .CreateDefaultBuilder() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureMauiHandlers(handlers => handlers.AddHandler()) .Build(); - var handler = host.Handlers.GetHandler(typeof(IViewStub)); + var handler = mauiApp.Services.GetRequiredService().GetHandler(typeof(IViewStub)); Assert.NotNull(handler); Assert.IsType(handler); @@ -51,12 +46,11 @@ public void CanRegisterAndGetHandlerUsingType() [Fact] public void CanRegisterAndGetHandler() { - var host = AppHost - .CreateDefaultBuilder() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureMauiHandlers(handlers => handlers.AddHandler()) .Build(); - var handler = host.Handlers.GetHandler(); + var handler = mauiApp.Services.GetRequiredService().GetHandler(); Assert.NotNull(handler); Assert.IsType(handler); @@ -65,12 +59,11 @@ public void CanRegisterAndGetHandler() [Fact] public void CanRegisterAndGetHandlerWithType() { - var host = AppHost - .CreateDefaultBuilder() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandler(typeof(IViewStub), typeof(ViewHandlerStub))) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureMauiHandlers(handlers => handlers.AddHandler(typeof(IViewStub), typeof(ViewHandlerStub))) .Build(); - var handler = host.Handlers.GetHandler(typeof(IViewStub)); + var handler = mauiApp.Services.GetRequiredService().GetHandler(typeof(IViewStub)); Assert.NotNull(handler); Assert.IsType(handler); @@ -84,12 +77,11 @@ public void CanRegisterAndGetHandlerWithDictionary() { typeof(IViewStub), typeof(ViewHandlerStub) } }; - var host = AppHost - .CreateDefaultBuilder() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandlers(dic)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureMauiHandlers(handlers => handlers.AddHandlers(dic)) .Build(); - var handler = host.Handlers.GetHandler(typeof(IViewStub)); + var handler = mauiApp.Services.GetRequiredService().GetHandler(typeof(IViewStub)); Assert.NotNull(handler); Assert.IsType(handler); @@ -98,12 +90,11 @@ public void CanRegisterAndGetHandlerWithDictionary() [Fact] public void CanRegisterAndGetHandlerForConcreteType() { - var host = AppHost - .CreateDefaultBuilder() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureMauiHandlers(handlers => handlers.AddHandler()) .Build(); - var handler = host.Handlers.GetHandler(typeof(ViewStub)); + var handler = mauiApp.Services.GetRequiredService().GetHandler(typeof(ViewStub)); Assert.NotNull(handler); Assert.IsType(handler); @@ -112,17 +103,16 @@ public void CanRegisterAndGetHandlerForConcreteType() [Fact] public void CanChangeHandlerRegistration() { - var host = AppHost - .CreateDefaultBuilder() - .ConfigureMauiHandlers((_, handlers) => handlers.AddHandler()) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureMauiHandlers(handlers => handlers.AddHandler()) .Build(); - var specificHandler = host.Handlers.GetHandler(typeof(ButtonStub)); + var specificHandler = mauiApp.Services.GetRequiredService().GetHandler(typeof(ButtonStub)); Assert.IsType(specificHandler); - host.Handlers.GetCollection().AddHandler(); + mauiApp.Services.GetRequiredService().GetCollection().AddHandler(); - var alternateHandler = host.Handlers.GetHandler(typeof(ButtonStub)); + var alternateHandler = mauiApp.Services.GetRequiredService().GetHandler(typeof(ButtonStub)); Assert.IsType(alternateHandler); } } diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderImageSourceTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderImageSourceTests.cs index 92f4b5987bc8..4b8abb5e2b71 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderImageSourceTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderImageSourceTests.cs @@ -13,11 +13,12 @@ public class HostBuilderImageSourceTests [InlineData(typeof(FileImageSourceStub))] public void CanRetrieveFileUsingInterfaceImageSource(Type type) { - var host = new AppHostBuilder() - .ConfigureImageSources() - .Build(); + var builder = MauiApp + .CreateBuilder() + .ConfigureImageSources(); + var mauiApp = builder.Build(); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); Assert.NotNull(images); var imageSourceService = images.GetRequiredImageSourceService(type); @@ -28,15 +29,16 @@ public void CanRetrieveFileUsingInterfaceImageSource(Type type) [Fact] public void CanRetrieveFontUsingInterfaceImageSource() { - var host = new AppHostBuilder() + var builder = MauiApp + .CreateBuilder() .ConfigureFonts() - .ConfigureImageSources() - .Build(); + .ConfigureImageSources(); + var mauiApp = builder.Build(); - var manager = host.Services.GetRequiredService(); + var manager = mauiApp.Services.GetRequiredService(); Assert.NotNull(manager); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); Assert.NotNull(images); var imageSourceService = images.GetRequiredImageSourceService(); @@ -49,15 +51,16 @@ public void CanRetrieveFontUsingInterfaceImageSource() [Fact] public void CanRetrieveFontUsingConcreteImageSource() { - var host = new AppHostBuilder() + var builder = MauiApp + .CreateBuilder() .ConfigureFonts() - .ConfigureImageSources() - .Build(); + .ConfigureImageSources(); + var mauiApp = builder.Build(); - var manager = host.Services.GetRequiredService(); + var manager = mauiApp.Services.GetRequiredService(); Assert.NotNull(manager); - var images = host.Services.GetRequiredService(); + var images = mauiApp.Services.GetRequiredService(); Assert.NotNull(images); var imageSourceService = images.GetRequiredImageSourceService(); diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderServiceBuilderTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderServiceBuilderTests.cs index a2b3eb9158c3..51412fed114d 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderServiceBuilderTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderServiceBuilderTests.cs @@ -16,16 +16,15 @@ public class HostBuilderServiceBuilderTests [Fact] public void MultipleServicesAreRegisteredWithoutBuilder() { - var host = new AppHostBuilder() - .UseServiceProviderFactory(new MicrosoftExtensionsServiceProviderFactory()) - .ConfigureServices((context, services) => services.AddSingleton(new MappingService("key 1", "value 1"))) - .ConfigureServices((context, services) => services.AddSingleton(new MappingService("key 2", "value 2"))) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddSingleton(new MappingService("key 1", "value 1")); + builder.Services.AddSingleton(new MappingService("key 2", "value 2")); + var mauiApp = builder.Build(); - var services = host.Services.GetServices().ToArray(); + var mappingServices = mauiApp.Services.GetServices().ToArray(); - Assert.Equal(2, services.Length); - Assert.NotEqual(services[0], services[1]); + Assert.Equal(2, mappingServices.Length); + Assert.NotEqual(mappingServices[0], mappingServices[1]); Assert.Equal(new[] { "value 1" }, Get("key 1")); Assert.Equal(new[] { "value 2" }, Get("key 2")); @@ -33,164 +32,29 @@ public void MultipleServicesAreRegisteredWithoutBuilder() IEnumerable Get(string key) { - foreach (var service in services) + foreach (var service in mappingServices) foreach (var value in service.Get(key)) yield return value; } } - [Fact] - public void OnlyOneServiceIsRegistered() - { - var host = new AppHostBuilder() - .UseServiceProviderFactory(new MicrosoftExtensionsServiceProviderFactory()) - .ConfigureServices((context, builder) => builder.Add("key 1", "value 1")) - .ConfigureServices((context, builder) => builder.Add("key 2", "value 2")) - .Build(); - - var services = host.Services.GetServices().ToArray(); - - var service = Assert.Single(services); - - Assert.Equal(new[] { "value 1" }, service.Get("key 1")); - Assert.Equal(new[] { "value 2" }, service.Get("key 2")); - Assert.Empty(service.Get("key 3")); - } - - [Fact] - public void EventsAreInvokedOnceAndInTheRightOrder() - { - var delegateInvoked = 0; - var order = new List(); - - Assert.Equal(0, CountingServiceBuilder.Count); - - var host = new AppHostBuilder() - .ConfigureServices((context, builder) => - { - delegateInvoked++; - order.Add("Delegate1"); - - builder.BuildInvoked += (context, services) => - { - order.Add("Build"); - Assert.NotNull(context); - Assert.NotNull(services); - }; - - builder.ConfigureInvoked += (context, services) => - { - order.Add("Configure"); - Assert.NotNull(context); - Assert.NotNull(services); - }; - }) - .ConfigureServices((context, builder) => - { - delegateInvoked++; - order.Add("Delegate2"); - }) - .ConfigureServices((context, builder) => - { - delegateInvoked++; - order.Add("Delegate3"); - }) - .Build(); - - Assert.Equal(3, delegateInvoked); - Assert.Equal(new[] { "Delegate1", "Delegate2", "Delegate3", "Build", "Configure" }, order); - - Assert.Equal(1, CountingServiceBuilder.Count); - } - - [Fact] - public void EventsAreInvokedWithTheCorrectParameters() - { - var host = new AppHostBuilder() - .ConfigureServices((context, builder) => - { - Assert.NotNull(context); - Assert.NotNull(builder); - builder.BuildInvoked += (context, services) => - { - Assert.NotNull(context); - Assert.NotNull(services); - }; - builder.ConfigureInvoked += (context, services) => - { - Assert.NotNull(context); - Assert.NotNull(services); - }; - }) - .Build(); - } [Fact] public void AppConfigurationReachesBuilder() { - string buildValue = null; - string configureValue = null; - - var host = new AppHostBuilder() + var builder = MauiApp.CreateBuilder(); + builder.Host .ConfigureAppConfiguration((_, builder) => { builder.AddInMemoryCollection(new Dictionary { { "key 1", "value 1" }, }); - }) - .ConfigureServices((context, builder) => - { - builder.BuildInvoked += (context, services) => - { - buildValue = context.Configuration["key 1"]; - }; - builder.ConfigureInvoked += (context, services) => - { - configureValue = context.Configuration["key 1"]; - }; - }) - .Build(); - - Assert.Equal("value 1", buildValue); - Assert.Equal("value 1", configureValue); - } + }); - class CountingServiceBuilder : ServiceBuilderStub - { - public static int Count { get; private set; } = 0; + var appConfigValue = builder.Configuration["key 1"]; + var mauiApp = builder.Build(); - public CountingServiceBuilder() - { - Count++; - } - } - - class MultipleRegistrationBuilder : MappingService, IMauiServiceBuilder - { - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - services.AddSingleton(this); - } - - public void Configure(HostBuilderContext context, IServiceProvider services) - { - } - } - - class ServiceBuilderStub : IMauiServiceBuilder - { - public event Action BuildInvoked; - public event Action ConfigureInvoked; - - public void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - BuildInvoked?.Invoke(context, services); - } - - public void Configure(HostBuilderContext context, IServiceProvider services) - { - ConfigureInvoked?.Invoke(context, services); - } + Assert.Equal("value 1", appConfigValue); } class MicrosoftExtensionsServiceProviderFactory : IServiceProviderFactory diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs index ebd6c50b01c6..26ab50946be0 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs @@ -13,49 +13,42 @@ public class HostBuilderServicesTests [Fact] public void CanGetServices() { - var host = new AppHostBuilder() - .Build(); + var builder = MauiApp.CreateBuilder(); + var mauiApp = builder.Build(); - Assert.NotNull(host); - Assert.NotNull(host.Services); + Assert.NotNull(mauiApp.Services); } [Fact] public void GetServiceThrowsWhenConstructorParamTypesWereNotRegistered() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => services.AddTransient()) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - Assert.Throws(() => host.Services.GetService()); + Assert.Throws(() => mauiApp.Services.GetService()); } [Fact] public void GetServiceThrowsWhenNoPublicConstructors() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => services.AddTransient()) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var ex = Assert.Throws(() => host.Services.GetService()); - Assert.Contains("public or internal constructors", ex.Message); + var ex = Assert.Throws(() => mauiApp.Services.GetService()); + Assert.Contains("suitable constructor", ex.Message); } [Fact] public void GetServiceHandlesFirstOfMultipleConstructors() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var service = host.Services.GetService(); + var service = mauiApp.Services.GetService(); var foobar = Assert.IsType(service); Assert.IsType(foobar.Foo); @@ -64,16 +57,12 @@ public void GetServiceHandlesFirstOfMultipleConstructors() [Fact] public void GetServiceHandlesSecondOfMultipleConstructors() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var service = host.Services.GetService(); + var service = mauiApp.Services.GetService(); var foobar = Assert.IsType(service); Assert.IsType(foobar.Bar); @@ -82,15 +71,11 @@ public void GetServiceHandlesSecondOfMultipleConstructors() [Fact] public void GetServiceHandlesUsesCorrectCtor_DefaultWithNothing() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var service = host.Services.GetService(); + var service = mauiApp.Services.GetService(); var trio = Assert.IsType(service); Assert.Null(trio.Foo); @@ -102,16 +87,12 @@ public void GetServiceHandlesUsesCorrectCtor_DefaultWithNothing() [Fact] public void GetServiceHandlesUsesCorrectCtor_DefaultWithBar() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var service = host.Services.GetService(); + var service = mauiApp.Services.GetService(); var trio = Assert.IsType(service); Assert.Null(trio.Foo); @@ -123,16 +104,12 @@ public void GetServiceHandlesUsesCorrectCtor_DefaultWithBar() [Fact] public void GetServiceHandlesUsesCorrectCtor_Foo() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var service = host.Services.GetService(); + var service = mauiApp.Services.GetService(); var trio = Assert.IsType(service); Assert.IsType(trio.Foo); @@ -144,17 +121,13 @@ public void GetServiceHandlesUsesCorrectCtor_Foo() [Fact] public void GetServiceHandlesUsesCorrectCtor_FooWithCat() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - }) - .Build(); - - var service = host.Services.GetService(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + var service = mauiApp.Services.GetService(); var trio = Assert.IsType(service); Assert.IsType(trio.Foo); @@ -166,17 +139,13 @@ public void GetServiceHandlesUsesCorrectCtor_FooWithCat() [Fact] public void GetServiceHandlesUsesCorrectCtor_FooBar() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - }) - .Build(); - - var service = host.Services.GetService(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + var service = mauiApp.Services.GetService(); var trio = Assert.IsType(service); Assert.IsType(trio.Foo); @@ -188,18 +157,14 @@ public void GetServiceHandlesUsesCorrectCtor_FooBar() [Fact] public void GetServiceHandlesUsesCorrectCtor_FooBarCat() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - }) - .Build(); - - var service = host.Services.GetService(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + var service = mauiApp.Services.GetService(); var trio = Assert.IsType(service); Assert.IsType(trio.Foo); @@ -211,17 +176,13 @@ public void GetServiceHandlesUsesCorrectCtor_FooBarCat() [Fact] public void GetServiceCanReturnTypesThatHaveConstructorParams() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - }) - .Build(); - - var service = host.Services.GetService(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + var service = mauiApp.Services.GetService(); var foobar = Assert.IsType(service); Assert.IsType(foobar.Foo); @@ -231,15 +192,11 @@ public void GetServiceCanReturnTypesThatHaveConstructorParams() [Fact] public void GetServiceCanReturnTypesThatHaveUnregisteredConstructorParamsButHaveDefaultValues() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var foo = host.Services.GetService(); + var foo = mauiApp.Services.GetService(); Assert.NotNull(foo); @@ -251,16 +208,12 @@ public void GetServiceCanReturnTypesThatHaveUnregisteredConstructorParamsButHave [Fact] public void GetServiceCanReturnTypesThatHaveRegisteredConstructorParamsAndHaveDefaultValues() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var foo = host.Services.GetService(); + var foo = mauiApp.Services.GetService(); Assert.NotNull(foo); @@ -273,15 +226,11 @@ public void GetServiceCanReturnTypesThatHaveRegisteredConstructorParamsAndHaveDe [Fact] public void GetServiceCanReturnTypesThatHaveSystemDefaultValues() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - }) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - var foo = host.Services.GetService(); + var foo = mauiApp.Services.GetService(); Assert.NotNull(foo); @@ -293,17 +242,13 @@ public void GetServiceCanReturnTypesThatHaveSystemDefaultValues() [Fact] public void GetServiceCanReturnEnumerableParams() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - }) - .Build(); - - var service = host.Services.GetService(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + var service = mauiApp.Services.GetService(); var foobar = Assert.IsType(service); var serviceTypes = foobar.Foos @@ -316,75 +261,63 @@ public void GetServiceCanReturnEnumerableParams() [Fact] public void WillRetrieveDifferentTransientServices() { - var host = new AppHostBuilder() - .ConfigureServices((ctx, services) => services.AddTransient()) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); - AssertTransient(host.Services); + AssertTransient(mauiApp.Services); } [Fact] public void WillRetrieveSameSingletonServices() { - var host = new AppHostBuilder() - .ConfigureServices((ctx, services) => services.AddSingleton()) - .Build(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddSingleton(); + var mauiApp = builder.Build(); - AssertSingleton(host.Services); + AssertSingleton(mauiApp.Services); } [Fact] public void WillRetrieveMixedServices() { - var host = new AppHostBuilder() - .ConfigureServices((ctx, services) => - { - services.AddSingleton(); - services.AddTransient(); - }) - .Build(); - - AssertSingleton(host.Services); - AssertTransient(host.Services); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + AssertSingleton(mauiApp.Services); + AssertTransient(mauiApp.Services); } [Fact] public void WillRetrieveEnumerables() { - var host = new AppHostBuilder() - .ConfigureServices((ctx, services) => - { - services.AddTransient(); - services.AddTransient(); - }) - .Build(); - - var services = host.Services + var builder = MauiApp.CreateBuilder(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + var mauiApp = builder.Build(); + + var fooServices = mauiApp.Services .GetServices() .ToArray(); - Assert.Equal(2, services.Length); + Assert.Equal(2, fooServices.Length); - var serviceTypes = services + var serviceTypes = fooServices .Select(s => s.GetType().FullName) .ToArray(); Assert.Contains(typeof(FooService).FullName, serviceTypes); Assert.Contains(typeof(FooService2).FullName, serviceTypes); } - [Theory] - //[InlineData(true)] // TODO: The MAUI provider does not support generic args - [InlineData(false)] - public void CanCreateLogger(bool ctorInjection) + [Fact] + public void CanCreateLogger() { - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(ctorInjection) - .ConfigureServices((ctx, services) => - { - services.AddLogging(logging => logging.AddConsole()); - }) - .Build(); - - var factory = host.Services.GetRequiredService(); + var builder = MauiApp.CreateBuilder(); + builder.Services.AddLogging(logging => logging.AddConsole()); + var mauiApp = builder.Build(); + + var factory = mauiApp.Services.GetRequiredService(); var logger = factory.CreateLogger(); diff --git a/src/Core/tests/UnitTests/ImageSource/ImageSourceServiceTests.cs b/src/Core/tests/UnitTests/ImageSource/ImageSourceServiceTests.cs index 02196e7b4278..2c5769e463be 100644 --- a/src/Core/tests/UnitTests/ImageSource/ImageSourceServiceTests.cs +++ b/src/Core/tests/UnitTests/ImageSource/ImageSourceServiceTests.cs @@ -27,15 +27,11 @@ public void ResolvesToConcreteTypeOverInterface() private IImageSourceServiceProvider CreateImageSourceServiceProvider(Action configure) { - - var host = new AppHostBuilder() - .UseMauiServiceProviderFactory(true) + var mauiApp = MauiApp.CreateBuilder() .ConfigureImageSources(configure) .Build(); - var services = host.Services; - - var provider = services.GetRequiredService(); + var provider = mauiApp.Services.GetRequiredService(); return provider; } diff --git a/src/Core/tests/UnitTests/LifecycleEvents/LifecycleEventsTests.cs b/src/Core/tests/UnitTests/LifecycleEvents/LifecycleEventsTests.cs index 1350706399ee..a15c4916e544 100644 --- a/src/Core/tests/UnitTests/LifecycleEvents/LifecycleEventsTests.cs +++ b/src/Core/tests/UnitTests/LifecycleEvents/LifecycleEventsTests.cs @@ -11,22 +11,22 @@ public class LifecycleEventsTests [Fact] public void ConfigureLifecycleEventsRegistersService() { - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => { }) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => { }) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); Assert.NotNull(service); } [Fact] public void CanAddCustomEvent() { - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", () => { })) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", () => { })) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); Assert.True(service.ContainsEvent("TestEvent")); } @@ -34,11 +34,11 @@ public void CanAddCustomEvent() [Fact] public void CanAddDelegateEvent() { - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", param => param++)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", param => param++)) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); Assert.True(service.ContainsEvent("TestEvent")); } @@ -48,11 +48,11 @@ public void InvokingUnregisteredEventsDoesNotThrow() { var eventFired = 0; - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", () => eventFired++)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", () => eventFired++)) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); service.InvokeEvents("AnotherEvent"); @@ -64,11 +64,11 @@ public void EventsFireExactlyOnce() { var eventFired = 0; - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", () => eventFired++)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", () => eventFired++)) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); service.InvokeEvents("TestEvent"); @@ -81,11 +81,11 @@ public void DelegateEventsFireExactlyOnce() var eventFired = 0; var newValue = 0; - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", param => param + 1)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", param => param + 1)) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); service.InvokeEvents("TestEvent", del => { @@ -102,11 +102,11 @@ public void EventsMustBeInvokedUsingExactDelegate() { var eventFired = 0; - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", () => eventFired++)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", () => eventFired++)) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); Assert.True(service.ContainsEvent("TestEvent")); Assert.Empty(service.GetEventDelegates("TestEvent")); @@ -122,12 +122,12 @@ public void CanAddMultipleEventsViaMultipleConfigureLifecycleEvents() var event1Fired = 0; var event2Fired = 0; - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", () => event1Fired++)) - .ConfigureLifecycleEvents((_, builder) => builder.AddEvent("TestEvent", () => event2Fired++)) + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", () => event1Fired++)) + .ConfigureLifecycleEvents(builder => builder.AddEvent("TestEvent", () => event2Fired++)) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); service.InvokeEvents("TestEvent"); @@ -141,15 +141,15 @@ public void CanAddMultipleEventsViaBuilder() var event1Fired = 0; var event2Fired = 0; - var host = new AppHostBuilder() - .ConfigureLifecycleEvents((_, builder) => + var mauiApp = MauiApp.CreateBuilder() + .ConfigureLifecycleEvents(builder => { builder.AddEvent("TestEvent", () => event1Fired++); builder.AddEvent("TestEvent", () => event2Fired++); }) .Build(); - var service = host.Services.GetRequiredService(); + var service = mauiApp.Services.GetRequiredService(); service.InvokeEvents("TestEvent"); diff --git a/src/Essentials/samples/Samples/Platforms/Android/MainApplication.cs b/src/Essentials/samples/Samples/Platforms/Android/MainApplication.cs index 534e412216e6..538d339fce55 100644 --- a/src/Essentials/samples/Samples/Platforms/Android/MainApplication.cs +++ b/src/Essentials/samples/Samples/Platforms/Android/MainApplication.cs @@ -7,10 +7,12 @@ namespace Samples.Droid { [Application] - public class MainApplication : MauiApplication + public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Essentials/samples/Samples/Platforms/MacCatalyst/AppDelegate.cs b/src/Essentials/samples/Samples/Platforms/MacCatalyst/AppDelegate.cs index d7238aaad230..5ecef7672f03 100644 --- a/src/Essentials/samples/Samples/Platforms/MacCatalyst/AppDelegate.cs +++ b/src/Essentials/samples/Samples/Platforms/MacCatalyst/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Samples.iOS { [Register(nameof(AppDelegate))] - public partial class AppDelegate : MauiUIApplicationDelegate + public partial class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Essentials/samples/Samples/Platforms/Windows/App.xaml.cs b/src/Essentials/samples/Samples/Platforms/Windows/App.xaml.cs index 1b81e6b1062c..d1af1c35fbec 100644 --- a/src/Essentials/samples/Samples/Platforms/Windows/App.xaml.cs +++ b/src/Essentials/samples/Samples/Platforms/Windows/App.xaml.cs @@ -2,8 +2,9 @@ namespace Samples.UWP { - public class MiddleApp : MauiWinUIApplication + public class MiddleApp : MauiWinUIApplication { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } public partial class App : MiddleApp diff --git a/src/Essentials/samples/Samples/Platforms/iOS/AppDelegate.cs b/src/Essentials/samples/Samples/Platforms/iOS/AppDelegate.cs index d7238aaad230..5ecef7672f03 100644 --- a/src/Essentials/samples/Samples/Platforms/iOS/AppDelegate.cs +++ b/src/Essentials/samples/Samples/Platforms/iOS/AppDelegate.cs @@ -4,7 +4,8 @@ namespace Samples.iOS { [Register(nameof(AppDelegate))] - public partial class AppDelegate : MauiUIApplicationDelegate + public partial class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Essentials/samples/Samples/Startup.cs b/src/Essentials/samples/Samples/Startup.cs index b83902b0d15d..4b2cddbfa14c 100644 --- a/src/Essentials/samples/Samples/Startup.cs +++ b/src/Essentials/samples/Samples/Startup.cs @@ -1,14 +1,15 @@ using Microsoft.Maui; using Microsoft.Maui.Controls.Hosting; -using Microsoft.Maui.Hosting; using Microsoft.Maui.LifecycleEvents; namespace Samples { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { + var appBuilder = MauiApp.CreateBuilder(); + #if WINDOWS Microsoft.Maui.Essentials.Platform.MapServiceToken = "RJHqIE53Onrqons5CNOx~FrDr3XhjDTyEXEjng-CRoA~Aj69MhNManYUKxo6QcwZ0wmXBtyva0zwuHB04rFYAPf7qqGJ5cHb03RCDw1jIW8l"; @@ -31,9 +32,11 @@ public void Configure(IAppHostBuilder appBuilder) .AddWindows(windows => windows .OnLaunched((app, e) => Microsoft.Maui.Essentials.Platform.OnLaunched(e))); - #endif +#endif }) .UseMauiApp(); + + return appBuilder.Build(); } } } diff --git a/src/Essentials/test/DeviceTests/Platforms/Windows/App.xaml.cs b/src/Essentials/test/DeviceTests/Platforms/Windows/App.xaml.cs index b62e3042cd07..e709b6f7b89d 100644 --- a/src/Essentials/test/DeviceTests/Platforms/Windows/App.xaml.cs +++ b/src/Essentials/test/DeviceTests/Platforms/Windows/App.xaml.cs @@ -22,7 +22,8 @@ public App() } } - public class MiddleApp : MauiWinUIApplication + public class MiddleApp : MauiWinUIApplication { - } + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } } diff --git a/src/Essentials/test/DeviceTests/Startup.cs b/src/Essentials/test/DeviceTests/Startup.cs index 2430f206b736..77a85b2f41ec 100644 --- a/src/Essentials/test/DeviceTests/Startup.cs +++ b/src/Essentials/test/DeviceTests/Startup.cs @@ -5,10 +5,11 @@ namespace Microsoft.Maui.Essentials.DeviceTests { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { + var appBuilder = MauiApp.CreateBuilder(); appBuilder .ConfigureLifecycleEvents(life => { @@ -26,7 +27,7 @@ public void Configure(IAppHostBuilder appBuilder) { Assemblies = { - typeof(Startup).Assembly + typeof(MauiProgram).Assembly }, SkipCategories = Traits .GetSkipTraits() @@ -40,6 +41,8 @@ public void Configure(IAppHostBuilder appBuilder) RequiresUIContext = true, }) .UseVisualRunner(); + + return appBuilder.Build(); } } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-blazor/MauiApp1/Startup.cs b/src/Templates/src/templates/maui-blazor/MauiApp1/MauiProgram.cs similarity index 66% rename from src/Templates/src/templates/maui-blazor/MauiApp1/Startup.cs rename to src/Templates/src/templates/maui-blazor/MauiApp1/MauiProgram.cs index 61a369c43867..c9008939af35 100644 --- a/src/Templates/src/templates/maui-blazor/MauiApp1/Startup.cs +++ b/src/Templates/src/templates/maui-blazor/MauiApp1/MauiProgram.cs @@ -11,23 +11,23 @@ namespace MauiApp1 { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { - appBuilder + var builder = MauiApp.CreateBuilder(); + builder .RegisterBlazorMauiWebView() - .UseMicrosoftExtensionsServiceProviderFactory() .UseMauiApp() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); - }) - .ConfigureServices(services => - { - services.AddBlazorWebView(); - services.AddSingleton(); }); + + builder.Services.AddBlazorWebView(); + builder.Services.AddSingleton(); + + return builder.Build(); } } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Android/MainApplication.cs b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Android/MainApplication.cs index 3c15169c57be..1fd323907a07 100644 --- a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Android/MainApplication.cs +++ b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Android/MainApplication.cs @@ -6,11 +6,13 @@ namespace MauiApp1 { [Application] - public class MainApplication : MauiApplication + public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs index 346c91335b0b..8babe50ba38f 100644 --- a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs +++ b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs @@ -4,7 +4,8 @@ namespace MauiApp1 { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Windows/App.xaml.cs b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Windows/App.xaml.cs index b7124be7b613..e4188ad7be9e 100644 --- a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Windows/App.xaml.cs +++ b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/Windows/App.xaml.cs @@ -21,7 +21,7 @@ public App() this.InitializeComponent(); } - protected override IStartup OnCreateStartup() => new Startup(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); protected override void OnLaunched(LaunchActivatedEventArgs args) { diff --git a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/iOS/AppDelegate.cs b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/iOS/AppDelegate.cs index 346c91335b0b..8babe50ba38f 100644 --- a/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/iOS/AppDelegate.cs +++ b/src/Templates/src/templates/maui-blazor/MauiApp1/Platforms/iOS/AppDelegate.cs @@ -4,7 +4,8 @@ namespace MauiApp1 { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-mobile/MauiApp1/Startup.cs b/src/Templates/src/templates/maui-mobile/MauiApp1/MauiProgram.cs similarity index 72% rename from src/Templates/src/templates/maui-mobile/MauiApp1/Startup.cs rename to src/Templates/src/templates/maui-mobile/MauiApp1/MauiProgram.cs index 21c97d6a40ba..3acde13b33e1 100644 --- a/src/Templates/src/templates/maui-mobile/MauiApp1/Startup.cs +++ b/src/Templates/src/templates/maui-mobile/MauiApp1/MauiProgram.cs @@ -8,16 +8,19 @@ namespace MauiApp1 { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { - appBuilder + var builder = MauiApp.CreateBuilder(); + builder .UseMauiApp() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); + + return builder.Build(); } } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Android/MainApplication.cs b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Android/MainApplication.cs index 3c15169c57be..1fd323907a07 100644 --- a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Android/MainApplication.cs +++ b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Android/MainApplication.cs @@ -6,11 +6,13 @@ namespace MauiApp1 { [Application] - public class MainApplication : MauiApplication + public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs index 346c91335b0b..8babe50ba38f 100644 --- a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs +++ b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/MacCatalyst/AppDelegate.cs @@ -4,7 +4,8 @@ namespace MauiApp1 { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Windows/App.xaml.cs b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Windows/App.xaml.cs index b7124be7b613..e4188ad7be9e 100644 --- a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Windows/App.xaml.cs +++ b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/Windows/App.xaml.cs @@ -21,7 +21,7 @@ public App() this.InitializeComponent(); } - protected override IStartup OnCreateStartup() => new Startup(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); protected override void OnLaunched(LaunchActivatedEventArgs args) { diff --git a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/iOS/AppDelegate.cs b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/iOS/AppDelegate.cs index 346c91335b0b..8babe50ba38f 100644 --- a/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/iOS/AppDelegate.cs +++ b/src/Templates/src/templates/maui-mobile/MauiApp1/Platforms/iOS/AppDelegate.cs @@ -4,7 +4,8 @@ namespace MauiApp1 { [Register("AppDelegate")] - public class AppDelegate : MauiUIApplicationDelegate + public class AppDelegate : MauiUIApplicationDelegate { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } } \ No newline at end of file diff --git a/src/TestUtils/samples/DeviceTests.Sample/Platforms/Windows/App.xaml.cs b/src/TestUtils/samples/DeviceTests.Sample/Platforms/Windows/App.xaml.cs index fcb61a40d1d9..f2bb99e8fde2 100644 --- a/src/TestUtils/samples/DeviceTests.Sample/Platforms/Windows/App.xaml.cs +++ b/src/TestUtils/samples/DeviceTests.Sample/Platforms/Windows/App.xaml.cs @@ -22,7 +22,8 @@ public App() } } - public class MiddleApp : MauiWinUIApplication + public class MiddleApp : MauiWinUIApplication { - } + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } } diff --git a/src/TestUtils/samples/DeviceTests.Sample/Startup.cs b/src/TestUtils/samples/DeviceTests.Sample/Startup.cs index fb9a2a0549ae..b14c923fa28c 100644 --- a/src/TestUtils/samples/DeviceTests.Sample/Startup.cs +++ b/src/TestUtils/samples/DeviceTests.Sample/Startup.cs @@ -1,18 +1,18 @@ -using Microsoft.Maui.Hosting; -using Microsoft.Maui.TestUtils.DeviceTests.Runners; +using Microsoft.Maui.TestUtils.DeviceTests.Runners; namespace Microsoft.Maui.TestUtils.DeviceTests.Sample { - public class Startup : IStartup + public static class MauiProgram { - public void Configure(IAppHostBuilder appBuilder) + public static MauiApp CreateMauiApp() { + var appBuilder = MauiApp.CreateBuilder(); appBuilder .ConfigureTests(new TestOptions { Assemblies = { - typeof(Startup).Assembly + typeof(MauiProgram).Assembly }, }) .UseHeadlessRunner(new HeadlessRunnerOptions @@ -20,6 +20,8 @@ public void Configure(IAppHostBuilder appBuilder) RequiresUIContext = true, }) .UseVisualRunner(); + + return appBuilder.Build(); } } } \ No newline at end of file diff --git a/src/TestUtils/src/DeviceTests.Runners.SourceGen/RunnerGenerator.cs b/src/TestUtils/src/DeviceTests.Runners.SourceGen/RunnerGenerator.cs index a40bd1f2765a..62d5f8ee2e74 100644 --- a/src/TestUtils/src/DeviceTests.Runners.SourceGen/RunnerGenerator.cs +++ b/src/TestUtils/src/DeviceTests.Runners.SourceGen/RunnerGenerator.cs @@ -85,7 +85,8 @@ protected void AddSource(string filename, string contents) string GenerateAndroidSource() { - var startupName = "Startup"; + var mauiProgramName = "MauiProgram"; + var mauiProgramFullName = @"global::" + RootNamespace + "." + mauiProgramName; var splash = ContainsSplashScreen ? @"Theme = ""@style/Maui.SplashTheme""," : ""; var appName = "MainApplication"; @@ -99,12 +100,14 @@ string GenerateAndroidSource() namespace " + RootNamespace + @" { [global::Android.App.Application] - partial class " + appName + @" : global::Microsoft.Maui.MauiApplication + partial class " + appName + @" : global::Microsoft.Maui.MauiApplication { public " + appName + @"(global::System.IntPtr handle, global::Android.Runtime.JniHandleOwnership ownership) : base(handle, ownership) { } + + protected override global::Microsoft.Maui.MauiApp CreateMauiApp() => " + mauiProgramFullName + @".CreateMauiApp(); } } #endif @@ -163,7 +166,8 @@ public partial class " + headlessActivityName + @" : global::Microsoft.Maui.Test string GenerateIosSource() { - var startupName = "Startup"; + var mauiProgramName = "MauiProgram"; + var mauiProgramFullName = @"global::" + RootNamespace + "." + mauiProgramName; var visualDelegateName = "VisualRunnerAppDelegate"; var headlessDelegateName = "HeadlessRunnerAppDelegate"; @@ -196,8 +200,9 @@ static void Main(global::System.String[] args) namespace " + RootNamespace + @" { [global::Foundation.Register(""" + visualDelegateName + @""")] - partial class " + visualDelegateName + @" : global::Microsoft.Maui.MauiUIApplicationDelegate + partial class " + visualDelegateName + @" : global::Microsoft.Maui.MauiUIApplicationDelegate { + protected override global::Microsoft.Maui.MauiApp CreateMauiApp() => " + mauiProgramFullName + @".CreateMauiApp(); } } #endif @@ -206,8 +211,10 @@ partial class " + visualDelegateName + @" : global::Microsoft.Maui.MauiUIApplica namespace " + RootNamespace + @" { [global::Foundation.Register(""" + headlessDelegateName + @""")] - partial class " + headlessDelegateName + @" : global::Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner.MauiTestApplicationDelegate + partial class " + headlessDelegateName + @" : global::Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner.MauiTestApplicationDelegate { + + protected override global::Microsoft.Maui.MauiApp CreateMauiApp() => " + mauiProgramFullName + @".CreateMauiApp(); } } #endif diff --git a/src/TestUtils/src/DeviceTests.Runners/AppHostBuilderExtensions.cs b/src/TestUtils/src/DeviceTests.Runners/AppHostBuilderExtensions.cs index c5036fc44709..1159069966c2 100644 --- a/src/TestUtils/src/DeviceTests.Runners/AppHostBuilderExtensions.cs +++ b/src/TestUtils/src/DeviceTests.Runners/AppHostBuilderExtensions.cs @@ -10,14 +10,14 @@ namespace Microsoft.Maui.TestUtils.DeviceTests.Runners { public static class AppHostBuilderExtensions { - public static IAppHostBuilder ConfigureTests(this IAppHostBuilder appHostBuilder, TestOptions options) + public static MauiAppBuilder ConfigureTests(this MauiAppBuilder appHostBuilder, TestOptions options) { - appHostBuilder.ConfigureServices(services => services.AddSingleton(options)); + appHostBuilder.Services.AddSingleton(options); return appHostBuilder; } - public static IAppHostBuilder UseVisualRunner(this IAppHostBuilder appHostBuilder) + public static MauiAppBuilder UseVisualRunner(this MauiAppBuilder appHostBuilder) { appHostBuilder.UseMauiApp(svc => new MauiVisualRunnerApp( svc.GetRequiredService(), @@ -26,18 +26,15 @@ public static IAppHostBuilder UseVisualRunner(this IAppHostBuilder appHostBuilde return appHostBuilder; } - public static IAppHostBuilder UseHeadlessRunner(this IAppHostBuilder appHostBuilder, HeadlessRunnerOptions options) + public static MauiAppBuilder UseHeadlessRunner(this MauiAppBuilder appHostBuilder, HeadlessRunnerOptions options) { - appHostBuilder.ConfigureServices(services => - { - services.AddSingleton(options); + appHostBuilder.Services.AddSingleton(options); #if __ANDROID__ || __IOS__ || MACCATALYST - services.AddTransient(svc => new HeadlessTestRunner( + appHostBuilder.Services.AddTransient(svc => new HeadlessTestRunner( svc.GetRequiredService(), svc.GetRequiredService())); #endif - }); return appHostBuilder; } diff --git a/src/TestUtils/src/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs b/src/TestUtils/src/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs index 60231f656bb8..96231ebc4303 100644 --- a/src/TestUtils/src/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs +++ b/src/TestUtils/src/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs @@ -6,25 +6,54 @@ using UIKit; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Hosting; +using Microsoft.DotNet.XHarness.iOS.Shared.Execution; namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner { - public abstract class MauiTestApplicationDelegate : MauiTestApplicationDelegate - where TStartup : IStartup, new() + public abstract class MauiTestApplicationDelegate : UIApplicationDelegate { - protected override IAppHost OnBuildAppHost() + // TODO: https://github.com/xamarin/xamarin-macios/issues/12555 + readonly static string[] EnvVarNames = { + "NUNIT_AUTOSTART", + "NUNIT_AUTOEXIT", + "NUNIT_ENABLE_NETWORK", + "DISABLE_SYSTEM_PERMISSION_TESTS", + "NUNIT_HOSTNAME", + "NUNIT_TRANSPORT", + "NUNIT_LOG_FILE", + "NUNIT_HOSTPORT", + "USE_TCP_TUNNEL", + "RUN_END_TAG", + "NUNIT_ENABLE_XML_OUTPUT", + "NUNIT_ENABLE_XML_MODE", + "NUNIT_XML_VERSION", + "NUNIT_SORTNAMES", + "NUNIT_RUN_ALL", + "NUNIT_SKIPPED_METHODS", + "NUNIT_SKIPPED_CLASSES", + }; + + readonly static Dictionary EnvVars = new(); + + static MauiTestApplicationDelegate() { - var startup = new TStartup(); + // copy into dictionary for later + foreach (var envvar in EnvVarNames) + { + EnvVars[envvar] = Environment.GetEnvironmentVariable(envvar); + } + } - return startup - .CreateAppHostBuilder() - .ConfigureUsing(startup) - .Build(); + static void SetEnvironmentVariables() + { + // read from dictionary + foreach (var envvar in EnvVars) + { + Console.WriteLine($" {envvar.Key} = '{envvar.Value}'"); + Environment.SetEnvironmentVariable(envvar.Key, envvar.Value); + } } - } - public abstract class MauiTestApplicationDelegate : UIApplicationDelegate - { public static bool IsHeadlessRunner(string[] args) { // usually means this is from xharness @@ -46,13 +75,15 @@ protected MauiTestApplicationDelegate() public override UIWindow? Window { get; set; } - protected abstract IAppHost OnBuildAppHost(); + protected abstract MauiApp CreateMauiApp(); public override bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) { - var host = OnBuildAppHost(); + var mauiApp = CreateMauiApp(); + Services = mauiApp.Services; + + SetEnvironmentVariables(); - Services = host.Services; Options = Services.GetRequiredService(); RunnerOptions = Services.GetRequiredService(); diff --git a/src/TestUtils/src/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj b/src/TestUtils/src/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj index dc8e2ca21039..8f2329f16593 100644 --- a/src/TestUtils/src/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj +++ b/src/TestUtils/src/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj @@ -19,7 +19,7 @@ - +