Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the Initialization of RxUI #1737

Open
glennawatson opened this issue Sep 1, 2018 · 8 comments
Open

Improve the Initialization of RxUI #1737

glennawatson opened this issue Sep 1, 2018 · 8 comments

Comments

@glennawatson
Copy link
Contributor

The Initialization of RxUI is a bit clunky at the moment

reactiveui/rfcs#14

Implement a change around the discussion in the RFC.

@glennawatson glennawatson added this to the 9.1.1 milestone Sep 1, 2018
@stale
Copy link

stale bot commented Oct 31, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want this issue progressed faster please start a conversation about raising a pull-request or coordinating your pull-request with a maintainer to get it merged. Understand that if folks like yourself don't contribute, ReactiveUI won't grow. You may or may not know this but ReactiveUI is maintained by unpaid volunteers. The maintainers put up a big marketing front but at it's core is a couple of passionate folks. ReactiveUI cares about open-source sustainability as maintainers have a serious load on their shoulders. Consumers shouldn't be naive in thinking that the latest update to a nuget package just magically materializes from the ethers. These things happen because our peers make them happen. No-one wants a tragedy of the commons situation. I urge you to get involved. Thank-you.

@glennawatson
Copy link
Contributor Author

Ana came up with a point that at the moment on the platforms it works it works.

On the original RFC there was the following comment

So instead of something like options.UseDefaults() we would add some additional verbosity by splitting options out into their own functions?

Like

RxUIOptions options = new RxUIOptions();
options.UsePlatformSchedulers();
options.UsePlatformBindingConverters();
options.UsePlatformActivationForViewFetcher();
// etc..
Presumably the equivalent to options.UseDefaults() is simply RxApp.Start()? So what's the difference between this:

RxApp.Start(new RxUIOptions());
and this:

RxApp.Start();
or even this:

RxApp.Start(null);
Is the first just an "empty" RxApp whereas the second is an RxApp with all of the default platform registrations? (and the last is an ArgumentNullException?) I think if that's the case we might want to look at something like RxApp.StartDefault() instead of RxApp.Start() just to help make the behavior less surprising.

Also, would we be able to call RxApp.Start() multiple times with different options? What sort of bugs would we run into if that was supported? I would like to be able to call Start() multiple times so I can use different options for different unit tests, but calling Start() twice in a normal application seems like something that we probably don't want to support.

Just thinking we'd probably want to take advantage of the Target Framework where we can with a generic "Start()", maybe we can make this a extension method against our NuGet packages.

Also our error messages have to be really consistent if we are in a non-initialized state if possible since at the moment the users really have to hunt the documentation.

@MovGP0
Copy link

MovGP0 commented Oct 11, 2019

why not use something like:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Splat.Microsoft.Extensions.DependencyInjection;
// ...

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
       using(var host = BuildHost(e.Args))
       {
           host.Start();
           var services = host.Services;
           services.UseMicrosoftDependencyResolver();

            using (var mainScope = services.CreateScope())
            {
               var mainWindow = (Window) mainScope.ServiceProvider
                   .GetRequiredService<IViewFor<MainWindowViewModel>();

                mainWindow.Show();
            }
        }
    }

    private IHost BuildHost(string[] args)
    {
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((builderContext, config) => {
                IHostEnvironment env = builderContext.HostEnvironment;
                config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

                // ...
            })
            .ConfigureServices(services => {
                // configure Splat to use Microsoft DI
                services.UseMicrosoftDependencyResolver();
                var resolver = Splat.Locator.CurrentMutable;
                resolver.InitializeSplat();
                resolver.InitializeReactiveUI();

                // add ReactiveUI 
                services.AddRxUI();

                services.AddTransient<MainWindowViewModel>();
                services.AddTransient<IViewFor<MainWindowViewModel>, MainWindow>();

                // TODO: configure other services (user controls, viewmodels, services)

                services.Configure<RxUIOptions>(options => {
                    options.UsePlatformSchedulers();
                    options.UsePlatformBindingConverters();
                    options.UsePlatformActivationForViewFetcher();
                });

                // TODO: configure other options
            })
            .ConfigureLogging(logger => {
                // TODO: configure logging
            })
            .Build();
    }
}

Using Splat in ViewModel:

public sealed MainViewModel : ReactiveObject, IActivatableViewModel 
{
        public MainViewModel()
        {
             var scopeFactory = Splat.Locator.Current.GetService<IServiceProvider>();

             this.WhenActivated(d => {
                 var scope = scopeFactory.CreateScope().DisposeWith(d);
                 var services = scope.ServiceProvider;

                 // get other services from services
            );
        }

        public ViewModelActivator Activator { get; } = new ViewModelActivator();
}

@RLittlesII
Copy link
Member

I think the over all plan is to give the flexibility of some of the .NET Core Startup concepts, but keep a simple RxApp.InitializeWPF() option. This would institute a huge breaking change and for consumers that go from not having to do anything to initialize ReactiveUI to having to provide an entire Startup file might feel overwhelming. So providing a default init method per platform that gives you OOTB what you get today is ideal.

@InquisitorJax
Copy link

I know this is an old thread, but do we have an eta on delivering this?
I think it preferable to have an init() method implemented rather than having to click continue 3 or 4 times for FileNotFoundException every time I start an app with CLR exception management turned on.
As an aside - I tried just unchecking the System.IO.FileNotFoundException (or adding a condition for ReactiveUI.dll) - but this doesn't seem to work :( (VS for Windows)

@glennawatson
Copy link
Contributor Author

Use

public static void SetRegistrationNamespaces(params RegistrationNamespace[] namespaces) => NamespacesToRegister = namespaces;
before any other call to RxUI and register the platforms you are using.

@InquisitorJax
Copy link

InquisitorJax commented May 16, 2022

Thanks @glennawatson
We're using Forms, and I've registered it as suggested:
PlatformRegistrationManager.SetRegistrationNamespaces(RegistrationNamespace.XamForms);

The result, however, is that it still moans about trying to find XamForms (but at least it doesn't about the other platforms :) )

image

Also, this seems to happen when running the iOS project, but I'm not seeing it when running Android (likely because the startup code does touch anything reactiveUI related).

@pellet
Copy link

pellet commented Jul 23, 2024

This fixed the issue for me in my MAUI project(targeting net8.0-windows10.0.19041.0), I just called this before creating a new page:
PlatformRegistrationManager.SetRegistrationNamespaces(RegistrationNamespace.Maui);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants