Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[Handlers,iOS,Android] Add AppLoader
Browse files Browse the repository at this point in the history
  • Loading branch information
rmarinho committed Oct 20, 2020
1 parent 947cb25 commit bf4dabb
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 53 deletions.
6 changes: 3 additions & 3 deletions src/Platform.Handlers/samples/Sample.Droid/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ protected override void OnCreate(Bundle savedInstanceState)

_page = FindViewById<ViewGroup>(Resource.Id.pageLayout);


var app = CreateAppBuilder()
.UserInit()
.ConfigureServices(ConfigureExtraServices)
//.RegisterHandler<IButton, CustomHandlers.CustomPinkTextButtonHandler>()
.Init<MyApp>();
_appBuilder.Start();
Add(app.CreateView());

var page = app.Services.GetRequiredService<IStartup>() as Xamarin.Forms.ContentPage;
Add(page.Content as IView);
}

void ConfigureExtraServices(HostBuilderContext ctx, IServiceCollection services)
Expand Down
9 changes: 4 additions & 5 deletions src/Platform.Handlers/samples/Sample.iOS/AppDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,17 @@ public override bool FinishedLaunching(UIApplication application, NSDictionary l
_window = new UIWindow();

var app = CreateAppBuilder()
.UserInit()
.ConfigureServices(ConfigureExtraServices)
//.RegisterHandler<IButton, CustomHandlers.CustomPinkTextButtonHandler>()
.RegisterHandler<IButton, CustomHandlers.CustomPinkTextButtonHandler>()
.Init<MyApp>();

_appBuilder.Start();
IView content = app.CreateView();

var page = app.Services.GetRequiredService<IStartup>() as Xamarin.Forms.ContentPage;

_window.RootViewController = new UIViewController
{
View = content.ToNative()
View = page.Content.ToNative()
};

_window.MakeKeyAndVisible();
Expand Down
9 changes: 4 additions & 5 deletions src/Platform.Handlers/samples/Sample/MyApp.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Sample.Pages;
using Xamarin.Platform;
using Sample.Services;
using Sample.ViewModel;
using Xamarin.Platform.Core;
using Xamarin.Platform.Hosting;

namespace Sample
{
public class MyApp : App
{
public MyApp(IHost host) : base(host)
{
}

public override IView CreateView()
public void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
var verticalStack = new Xamarin.Platform.VerticalStackLayout() { Spacing = 5, BackgroundColor = Color.AntiqueWhite };
var horizontalStack = new Xamarin.Platform.HorizontalStackLayout() { Spacing = 2 };
Expand Down
3 changes: 2 additions & 1 deletion src/Platform.Handlers/samples/Sample/Pages/MainPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
using Sample.Services;
using Sample.ViewModel;
using Xamarin.Forms;
using Xamarin.Platform.Hosting;

namespace Sample.Pages
{
public class MainPage : ContentPage
public class MainPage : ContentPage, IStartup
{
public MainPage(MainPageViewModel viewModel)
{
Expand Down
20 changes: 11 additions & 9 deletions src/Platform.Handlers/src/Xamarin.Platform.Handlers/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,29 @@ namespace Xamarin.Platform.Core
{
public abstract class App : IApp
{
protected App(IHost host)
IServiceProvider? _serviceProvider;
IHandlerServiceProvider? _handlerServiceProvider;
protected App()
{
Host = host;
Handlers = Services.GetRequiredService<IHandlerServiceProvider>();
Current = this;
}

public static App? Current { get; private set; }

public IHost Host { get; private set; }
public IServiceProvider? Services => _serviceProvider;

public IServiceProvider Services => Host.Services;

public IHandlerServiceProvider Handlers { get; private set; }

public abstract IView CreateView();
public IHandlerServiceProvider? Handlers => _handlerServiceProvider;

public static AppBuilder CreateDefaultBuilder()
{
var builder = new AppBuilder().CreateAppDefaults();
return builder;
}

public void SetServiceProvider(IServiceProvider provider)
{
_serviceProvider = provider;
_handlerServiceProvider = provider.GetRequiredService<IHandlerServiceProvider>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xamarin.Platform.Core;
using Xamarin.Platform.Handlers;

namespace Xamarin.Platform.Hosting
{
public class AppBuilder : IDisposable
public class AppBuilder : HostBuilder, IDisposable
{

readonly HandlerServiceCollection _handlersCollection = new HandlerServiceCollection();
readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
IHost? _host;
Expand All @@ -23,51 +21,52 @@ public class AppBuilder : IDisposable

public AppBuilder()
{
HostBuilder = new HostBuilder();
_cts = new CancellationTokenSource();
}

//We expose the Builder so we allow the user to do HostConnfiguration like Logging
public IHostBuilder HostBuilder { get; }

//CreateAppDefaults will set the common things like:
// - Set the ContentRoot
// - Register the basic handlers
public AppBuilder CreateAppDefaults()
{
HostBuilder.UseContentRoot(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
this.UseContentRoot(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
UseXamarinHandlers();
return this;
}

public AppBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
public new AppBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
{
_configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
return this;
}

public TApplication Init<TApplication>() where TApplication : class, IApp
{
BuildAndRegisterHandlersProvider(HostBuilder);

HostBuilder.ConfigureServices((context, collection) =>
BuildAndRegisterHandlersProvider();

var app = Activator.CreateInstance(typeof(TApplication));
base.ConfigureServices((context, services) =>
{
foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
{
configureServicesAction(context, collection);
configureServicesAction(context, services);
}
collection.AddSingleton<IHostLifetime, AppLifetime>();
collection.AddSingleton<IApp, TApplication>();
services.AddSingleton<IHostLifetime, AppLifetime>();
AppLoader.ConfigureAppServices<TApplication>(context, services, app);
});

RegisterAssemblies<TApplication>();

_host = HostBuilder.Build();


_host = Build();

(app as App)?.SetServiceProvider(_host.Services);

return (TApplication)_host.Services.GetRequiredService<IApp>();
}


public async void Start()
{
if (_host != null)
Expand Down Expand Up @@ -108,13 +107,13 @@ public AppBuilder UseXamarinHandlers()
});
return this;
}

IHostBuilder BuildAndRegisterHandlersProvider(IHostBuilder hostBuilder)
IHostBuilder BuildAndRegisterHandlersProvider()
{
var handlersProvider = _handlersCollection.BuildHandlerServiceProvider();
hostBuilder.ConfigureServices((context, collection) => collection.AddSingleton(handlersProvider));
ConfigureServices((context, collection) => collection.AddSingleton<IHandlerServiceProvider>(handlersProvider));

return hostBuilder;
return this;
}

void RegisterAssemblies<TApplication>() where TApplication : class, IApp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Xamarin.Platform.Hosting
{
public interface IStartup
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xamarin.Platform.Core;

namespace Xamarin.Platform.Hosting
{
internal static class AppLoader
{
public static void ConfigureAppServices<TApplication>(HostBuilderContext context, IServiceCollection services, object app) where TApplication : class, IApp
{
services.AddSingleton<IApp, TApplication>((serviceProvider) => (TApplication)app);

var startupType = typeof(TApplication);

var environmentName = context.HostingEnvironment.EnvironmentName;

var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false)
?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false);

if (servicesMethod != null)
servicesMethod.Invoke(app, new object[2] { context, services });
}

static MethodInfo? FindMethod(Type startupType, string methodName, string environmentName, Type? returnType = null, bool required = true)
{
var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName);
var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, "");

var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithEnv, StringComparison.OrdinalIgnoreCase)).ToList();
if (selectedMethods.Count > 1)
{
throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithEnv));
}
if (selectedMethods.Count == 0)
{
selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv, StringComparison.OrdinalIgnoreCase)).ToList();
if (selectedMethods.Count > 1)
{
throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv));
}
}

var methodInfo = selectedMethods.FirstOrDefault();
if (methodInfo == null)
{
if (required)
{
throw new InvalidOperationException(string.Format("A public method named '{0}' or '{1}' could not be found in the '{2}' type.",
methodNameWithEnv,
methodNameWithNoEnv,
startupType.FullName));

}
return null;
}
if (returnType != null && methodInfo.ReturnType != returnType)
{
if (required)
{
throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.",
methodInfo.Name,
startupType.FullName,
returnType.Name));
}
return null;
}
return methodInfo;
}

}
}
8 changes: 2 additions & 6 deletions src/Platform.Handlers/src/Xamarin.Platform.Handlers/IApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@ namespace Xamarin.Platform.Core
{
public interface IApp
{
IHost Host { get; }
IServiceProvider? Services { get; }

IServiceProvider Services { get; }

IHandlerServiceProvider Handlers { get; }

IView CreateView();
IHandlerServiceProvider? Handlers { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static AView ToNative(this IView view, Context context)
if (handler == null)
{
//handler = Registrar.Handlers.GetHandler(view.GetType());
handler = App.Current?.Handlers.GetHandler(view.GetType());
handler = App.Current?.Handlers?.GetHandler(view.GetType());

if (handler == null)
throw new System.Exception($"Handler not found for view {view}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static UIView ToNative(this IView view)
if (handler == null)
{
//handler = Registrar.Handlers.GetHandler(view.GetType());
handler = App.Current?.Handlers.GetHandler(view.GetType());
handler = App.Current?.Handlers?.GetHandler(view.GetType());

if (handler == null)
throw new System.Exception($"Handler not found for view {view}");
Expand Down

0 comments on commit bf4dabb

Please sign in to comment.