Simplify App Bootstrapping #918
Replies: 8 comments 16 replies
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
This comment has been hidden.
-
To update from this morning's discussion, the focus would be around an IAppBuilder that gives you access to the Application and startup Arguments with a focus on providing delegates for the IHostBuilder and a method to create the IHost. public interface IAppBuilder
{
Application App { get; }
string? Arguments { get; }
Window Window { get; }
IAppBuilder Configure(Action<IHostBuilder> configureHost);
IHost Build();
} This would be non-prescriptive in terms of requiring that the developer uses the Uno Navigation, etc, while still allowing more prescriptive extensions to be provided like: public static async Task<IHost> ShowAsync<TShell>(this IAppBuilder builder, Func<IAppBuilder, TShell, ContentControl> getRootElement)
where TShell : UIElement, new()
{
var appRoot = new TShell();
var navigationRoot = getRootElement(builder, appRoot);
builder.Window.Content = appRoot;
builder.Window.Activate();
return await builder.Window.InitializeNavigationAsync(
async () =>
{
return builder.Build();
},
navigationRoot: navigationRoot
);
} The advantage of this is that it can help clean up the Application's OnLaunched method like: protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
await this.Build(args)
.YourCustomExtension()
.Configure(builder => {
// call any existing IHostBuilder extensions
})
.ShowAsync<Shell>((builder, shell) => {
shell.SplashScreen.Initialize(builder.Window, args);
return shell.SplashScreen;
});
} |
Beta Was this translation helpful? Give feedback.
-
Could we rename IAppBuilder to IApplicationBuilder? If we keep the ShowAsync helper method in the framework, then in the template code I'd introduce a simple ShowAsync() extention method that contains the splashscreen callback init code to keep minimal api code as clean as possible. Other idea would've been to introduce some more of IContentControlHost interface that nav init would be aware of and that Shell vould implement to expose its innter SplashScreen..... Not sure if any clearner but we wouldn't neet the ballback. |
Beta Was this translation helpful? Give feedback.
-
Also, we'll need to make sure we have this bootstrapping working if Navigation is NOT enabled in the template. So to default back to the "vanilla" bootstrapping. |
Beta Was this translation helpful? Give feedback.
-
Latest iteration: public interface IApplicationBuilder
{
Application App { get; }
object? Arguments { get; }
Window Window { get; }
IApplicationBuilder Configure(Func<IHostBuilder, IHostBuilder> configureHost);
IHost? Host { get; }
IHost Build();
} Note that Configure takes a Func<IHostBuilder,IHostBuilder> which is more consistent with the hostbuilder pattern - the extensions methods typically accept a IHostBuilder and return a HostBuilder so they can be chained. The IApplicationBuilder also captures the IHost after the Build method has been called. ShowAsync method has no callback but has implicit knowledge of splashscreen -> would mean that it's part of Navigation.Toolkit. An improvement for this would be to offer an internal extension point so that when UseNavigationToolkit is called, we register interest in ShowAsync and do the appropriate initialization on the navigation root. public static class IApplicationBuilderExtensions
{
// Note: This extension belongs in Uno.Extensions.Navigation.UI/WinUI
public static async Task<IHost> ShowAsync<TShell>(this IApplicationBuilder builder)
where TShell : UIElement, new()
{
var appRoot = new TShell();
var navRoot = appRoot as ContentControl;
if (appRoot is IContentControlProvider contentProvider)
{
navRoot = contentProvider.ContentControl;
}
if (navRoot is ExtendedSplashScreen splashScreen &&
builder.Arguments is LaunchActivatedEventArgs launchArgs)
{
splashScreen.Initialize(builder.Window, launchArgs);
}
builder.Window.Content = appRoot;
builder.Window.Activate();
// TODO: This needs tidying up inside Navigation library so that there's a single
// InitializeNavigationAsync method that can make the determination on whether
// to use the logic from Navigation.Toolkit or not
if (navRoot is LoadingView loading)
{
// InitializeNavigationAsync from Navigation.Toolkit to deal with LoadingView
return await builder.Window.InitializeNavigationAsync(
async () =>
{
return builder.Build();
},
navigationRoot: loading
);
}
else
{
// InitializeNavigationAsync from Navigation that simply attaches navigation
return await builder.Window.InitializeNavigationAsync(
async () =>
{
return builder.Build();
},
navigationRoot: navRoot
);
}
}
} The resulting app startup is now simpler: protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
await this.CreateBuilder(args)
.YourCustomExtension() // These are extensions on IApplicationBuilder
.Configure(builder =>
builder
.UseXYZ() // These are extensions on IHostBuilder
.UseABX()
)
.ShowAsync<Shell>();
} This simplification uses the IContentControlProvider interface public interface IContentControlProvider
{
ContentControl ContentControl { get; }
} which is implemented by Shell public sealed partial class Shell : UserControl, IContentControlProvider
{
public ContentControl ContentControl => Splash;
public Shell()
{
this.InitializeComponent();
}
} |
Beta Was this translation helpful? Give feedback.
-
Why is AppBuilder.Argyments object? Cant it just be the string inside the LaunchEventArgs? Why is IHost needed to be captured? I think it's error prone as it can be null until you call BUild() so it's a bad idea. Otherwise am ok with moving ShowAsync to Nav.Toolkit, We still need a vanilla version without Navigation to create the Window and a simple shell maybe. So names cant conflict. |
Beta Was this translation helpful? Give feedback.
-
Would it scale better if we introduced IApplication right away, containing IHost, maybe the Window as well coming from IAppBuilder and whatever else we might need in the future? Am not a fan of creating abstractions for nothing but maybe it would scale better if we returned something generic already instead of Task... IApplication |
Beta Was this translation helpful? Give feedback.
-
Currently app startup is quite complex:
Goal is to simplify app bootstrapping to something like
Beta Was this translation helpful? Give feedback.
All reactions