Search code examples
pluginsmvvmcross

How to use an mvvmcross plugin such as the file plugin


I'm using mvvmcross version 6.4.1 to develop an app for IOS, Android, and WPF. I've searched all over for my to use plugins. There seems to be no code examples. The documentation said to install the nuget in both my core and ui application projects. Which I did. Is there any special IOC registration/setup/or loading that needs to be done before I can use the plugin and how do I go about using the plugin? Do they get injected in the constructor or Do I have to manually pull them from the IOC container or new () them up.

I've installed nuget for the File plugin into my WPF UI and Core project. I added the IMvxFileStore to one of my core project's service constructor thinking it automagically gets added to the DI container, but it doesn't seem to get injected.

namespace My.Core.Project.Services
{
   public class SomeService : ISomeService
   {
      private IMvxFileStore mvxFileStore;
      public SomeService(IMvxFileStore mvxFileStore)
      {
         this.mvxFileStore = mvxFileStore;
      }

      public string SomeMethod(string somePath)
      {
          mvxFileStore.TryReadTextFile(somePath, out string content);

          return content;
      }
   }
}

App.xaml.cs

using MvvmCross.Core;
using MvvmCross.Platforms.Wpf.Views;
...

public partial class App : MvxApplicatin
{
   protected override void RegisterSetup()
   {
      this.RegisterSetupType<Setup<Core.App>>();
   }
}

App.cs

using MvvmCross;
using MvvmCross.ViewModels;
using My.Core.Project.Services;

public class App: MvxApplication
{
  public override void Initialize()
  {
    Mvx.IocProvider.RegisterType<ISomeService, SomeService>();
    RegisterCustomAppStart<AppStart>();
  }
}

AppStart.cs

using MvvmCross.Exceptions;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
using My.Core.Project.ViewModels;
using System;
using System.Threading.Tasks;

....

public class AppStart : MvxAppStart
{
  public AppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService)
  {}

  public override Task NavigateToFirstViewModel(object hint = null)
  {
     try {
         return NavigationService.Navigate<FirstPageViewModel>();

     } catch {
         throw e.MvxWrap("Some error message {0}", typeof(FirstPageViewModel).Name);
     }
  }

}

Setup.cs in WPF project

using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Plugin.File;
using MvvmCross.Plugin.Json;
using MvvmCross.ViewModels;
using My.Wpf.Project.Services;
...

public class Setup<T> : MvxWpfSetup
{
    public Setup() : base() {}

    protected override IMvxApplication CreateApp()
    {
        return new Core.App();
    }

    protected override void InitializeFirstChange()
    {
        base.InitializeFirstChange();
        Mvx.IocProvider.RegisterType<ISomeWpfSpecificService>(() => new SomeWpfSpecificService());

    }

     protected override void InitializeLastChange()
     {
        base.InitializeLastChange();
     }
}

I'm expecting my service to load but instead, I get the error message MvxIoCResolveException: Failed to resolve parameter for parameter mvxJsonConverter of type IMvxJsonConverter

NOTE: I get the same error message for both File and Json plugin, The plugin that gets listed first in the constructor gets the error message when the app trys to load.

Am I properly using or loading the plugin?

UPDATE: I manually registered the Plugins in the UI Setup.cs and it is working but I am not sure if this is the proper way to do it.

WPF UI project Setup.cs

using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Plugin.File;
using MvvmCross.Plugin.Json;
using MvvmCross.ViewModels;
using My.Wpf.Project.Services;
...

public class Setup<T> : MvxWpfSetup
{
    public Setup() : base() {}

    protected override IMvxApplication CreateApp()
    {
        return new Core.App();
    }

    protected override void InitializeFirstChange()
    {
        base.InitializeFirstChange();
        Mvx.IocProvider.RegisterType<ISomeWpfSpecificService>(() => new SomeWpfSpecificService());
        Mvx.IoCProvider.RegisterType<IMvxFileStore, MvxFileStoreBase>();
        Mvx.IoCProvider.RegisterType<IMvxJsonConverter, MvxJsonConverter>();

    }

     protected override void InitializeLastChange()
     {
        base.InitializeLastChange();
     }
}



Solution

  • Yes you are using the plugin properly and I think that for now your solution to manually register your plugin is viable.

    The root of the problem is located in the MvxSetup class. This class contains the method LoadPlugins which is responsible for loading the MvvmCross plugins which are referenced by your UI project. This is how LoadPlugins determines what plugins to load:

    1. Get all assemblies that have been loaded into the execution context of the application domain.
    2. Find types within these assemblies which contain the MvxPluginAttribute.

    Now the problem occurs in step 1. In a .NET framework project, by default, your referenced assemblies won't be loaded into the execution context until you actually use them in your code. So if you don't use something from your MvvmCross.Plugin.File reference in your UI project it won't be loaded into your execution context and it won't be found in step 1 and thus it won't be registered by LoadPlugins. (good read: when does a .NET assembly Dependency get loaded)

    One way I have tested this is by doing this:

    protected override void InitializeFirstChance()
    {
        // Because a type of the MvvmCross.Plugin.File.Platforms.Wpf reference is
        // used here the assembly will now get loaded in the execution context
        var throwaway = typeof(Plugin);
    
        base.InitializeFirstChance();
    }
    

    With the above code you don't have to manually register the Plugin.

    There has been a pull request to fix this in the MvvmCross framework but this has been reverted later since it caused problems on other platforms.

    In other platforms the plugin assemblies will get loaded into the execution context without any tricks so I would say updating the MvvmCross documentation stating you have to register your plugin manually for WPF would be useful for other developers in the future.