[WIP] Fluent API + Micosoft.Extensions.Hosting for initialization of Xamarin.Forms by KSemenenko · Pull Request #8220 · xamarin/Xamarin.Forms (original) (raw)

Description of Change

What if we were to add a "Fluent API" for initialization of Xamarin.Forms? Instead of calling several Init methods that are not very intuitive or discoverable, we can use a fluent mechanism.
Based on @jamesmontemagno post
https://montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/

@nickrandolph thanks for the idea with the article.

Issues Resolved

var app = Forms.Create(this, bundle) .WithFlags("UseLegacyRenderers") .WithMaps() .WithVisualMaterial() .WithAppLinks() .UseStartup() .NativeConfigureHostConfiguration(c => { // do some native configuration }) .NativeConfigureServices((h, s) => { // do some native configuration s.AddSingleton<INativeDataService, MyNativeDataService>(); }) .NativeConfigureAppConfiguration((h, s) => { // do some native configuration }) .Build();

LoadApplication(app);

API Changes

new property in Application

IServiceProvider Application.ServiceProvider { get; set; }

new interfaces:

public interface IFormsBuilder { IFormsBuilder PostInit(Action action); IFormsBuilder PreInit(Action action);

void Init();

Application Build(Type app);
TApp Build<TApp>() where TApp : Application;
TApp Build<TApp>(Func<TApp> createApp) where TApp : Application;

IFormsBuilder UseStartup(Type startupType);
IFormsBuilder UseStartup<TStartup>() where TStartup : IStartup, new();
IFormsBuilder UseStartup<TStartup>(Func<TStartup> createStartup) where TStartup : IStartup;

IFormsBuilder NativeConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate);
IFormsBuilder NativeConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate);
IFormsBuilder NativeConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate);

}

public interface IStartup { void ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services); void ConfigureHostConfiguration(IConfigurationBuilder configurationBuilder); void ConfigureAppConfiguration(HostBuilderContext hostBuilderContext, IConfigurationBuilder configurationBuilder); }

Xamarin Forms Init part:

now we can write extensions for pre init:

public static class FormsBuilderExtensions { public static IFormsBuilder WithFlags(this IFormsBuilder init, params string[] flags) { return init.PreInit(() => Forms.SetFlags(flags)); } }

and we can write extensions for post init:

public static class FormsBuilderExtensions { public static IFormsBuilder WithAppLinks(this IFormsBuilder init, Activity activity) { return init.PostInit(() => AndroidAppLinks.Init(activity)); } }

DI + HostBuilder part:

We can create Startup.cs, and use it .UseStartup<Startup>()

public class Startup : IStartup { public void ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services) { if (hostBuilderContext.HostingEnvironment.IsDevelopment()) { var world = hostBuilderContext.Configuration["Hello"]; }

services.AddSingleton<IDataService, MyDataService>();
services.AddTransient<MyViewModel>();
services.AddTransient<MainPage>();
services.AddSingleton<App>();
}

public void ConfigureHostConfiguration(IConfigurationBuilder configurationBuilder)
{

}

public void ConfigureAppConfiguration(HostBuilderContext hostBuilderContext, IConfigurationBuilder configurationBuilder)
{

}

}

minimal call

var app = Forms.Create() .Build();

LoadApplication(app);

or just Init for backward compatibility

Forms.Create().Init(); var app = new App();

We can use the service provider to get instances of the class.

public App() { InitializeComponent(); MainPage = Application.ServiceProvider.GetService(); }

EmbeddedResourceLoader for load embedded resources.
An important condition is set the calling assembly by calling SetExecutingAssembly
this is done in the Build method in the FormsBuild class

public static class EmbeddedResourceLoader { public static void SetExecutingAssembly(Assembly assembly); public static byte[] GetEmbeddedResourceBytes(string resourceFileName); public static byte[] GetEmbeddedResourceBytes(string resourceFileName, Assembly assembly); public static string GetEmbeddedResourcePath(string resourceFileName); public static string GetEmbeddedResourcePath(string resourceFileName, Assembly assembly); public static Stream GetEmbeddedResourceStream(string resourceFileName); public static Stream GetEmbeddedResourceStream(string resourceFileName, Assembly assembly); public static string GetEmbeddedResourceString(string resourceFileName); public static string GetEmbeddedResourceString(string resourceFileName, Assembly assembly); public static ImageSource GetImageSource(string name); public static ImageSource GetImageSource(string name, Assembly assembly); }

Platforms Affected

.NET Standards 2.0 required.

Microsoft.Extensions.Hosting

Settings file should be added to the Forms project

Testing Procedure

Make sure that all test projects are launched as before.

PR Checklist