0
votes

I am developing an application in which I need to use platform specific services. I referred so many stuff in the internet and couldn't find a way to get this to work. Even the MvvmCross documentation has not been updated I guess.

My platform specific service is to pop up dialogs in each platform in their native appearances.

Since I am new to MvvmCross I couldn't think of a way to make this work. Any help would be highly appreciated.

Note: MvvmCross version is 6.4.2

IPlatformSpecificDialogs.cs in Core/Common Project

public interface IPlatformSpecificDialogs
{
    void Alert(string message, string title, string okButtonText);
}

IOSDialogs.cs in iOS Project

public class IOSDialogs : IPlatformSpecificDialogs
{
    public IOSDialogs()
    {
    }

    public void Alert(string message, string title, string okButtonText)
    {
        Console.WriteLine("worked");
    }
}

Setup.cs in iOS Project

public class Setup : MvxIosSetup<App>
{
    public Setup() : base()
    {
    }

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

    protected override void InitializeLastChance()
    {
        base.InitializeLastChance();
        Mvx.IoCProvider.RegisterSingleton<IPlatformSpecificDialogs>(() => new IOSDialogs());
    }
}

MyView.cs in iOS Project

[MvxFromStoryboard("Main")]
[MvxModalPresentation(WrapInNavigationController = false)]
public partial class MyView : BaseView<MyViewModel>
{
    public MyView(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
    }

    protected override void BindControllers()
    {
        var set = this.CreateBindingSet<MyView, MyViewModel>();
        set.Bind(AlertButton).To(vm => vm.AlertCommand);
        set.Apply();
    }
}

MyViewModel.cs in Core/Common Project

public class MyViewModel : BaseViewModel
{
    private readonly IPlatformSpecificDialogs _platformSpecificDialogs;

    public MyViewModel(IPlatformSpecificDialogs platformSpecificDialogs) : base()
    {
        _platformSpecificDialogs = platformSpecificDialogs;
    }

    public override Task Initialize()
    {
        return base.Initialize();
    }

    public IMvxCommand AlertCommand => new MvxCommand(PopupAlert);
    private void PopupAlert()
    {
        _platformSpecificDialogs.Alert("Title","Messege", "OK");
    }
}

Exception I get when I navigate to MyView

enter image description here

This is the Inner Exception Messege

Failed to resolve parameter for parameter platformSpecificDialogs of type IPlatformSpecificDialogs when creating MyProject.Core.ViewModels.MyViewModel. You may pass it as an argument.

This is the Inner Exception StackTrace

at MvvmCross.IoC.MvxIoCContainer.TryResolveParameter (System.Type type, System.Reflection.ParameterInfo parameterInfo, System.Object& parameterValue) [0x00020] in D:\a\1\s\MvvmCross\IoC\MvxIoCContainer.cs:686 at MvvmCross.IoC.MvxIoCContainer.GetIoCParameterValues (System.Type type, System.Reflection.ConstructorInfo selectedConstructor, System.Collections.Generic.IDictionary'2[TKey,TValue] arguments) [0x0003a] in D:\a\1\s\MvvmCross\IoC\MvxIoCContainer.cs:646 at MvvmCross.IoC.MvxIoCContainer.IoCConstruct (System.Type type, System.Collections.Generic.IDictionary'2[TKey,TValue] arguments) [0x0002b] in D:\a\1\s\MvvmCross\IoC\MvxIoCContainer.cs:416 at MvvmCross.IoC.MvxIoCContainer.IoCConstruct (System.Type type) [0x00000] in D:\a\1\s\MvvmCross\IoC\MvxIoCContainer.cs:363 at MvvmCross.IoC.MvxIoCProvider.IoCConstruct (System.Type type) [0x00000] in D:\a\1\s\MvvmCross\IoC\MvxIoCProvider.cs:149 at MvvmCross.ViewModels.MvxDefaultViewModelLocator.Load (System.Type viewModelType, MvvmCross.ViewModels.IMvxBundle parameterValues, MvvmCross.ViewModels.IMvxBundle savedState, MvvmCross.Navigation.EventArguments.IMvxNavigateEventArgs navigationArgs) [0x00000] in D:\a\1\s\MvvmCross\ViewModels\MvxDefaultViewModelLocator.cs:56

1

1 Answers

0
votes

Failed to resolve parameter for parameter platformSpecificDialogs of type IPlatformSpecificDialogs when creating MyProject.Core.ViewModels.MyViewModel. You may pass it as an argument.

The exception you are experiencing is due to a timing issue. To resolve it rather than placing the platform-specific dialogs registration in InitializeLastChance() place it in InitializeFirstChance()

Since I am new to MVVMCross I couldn't think of a way to make this work. Any help would be highly appreciated.

In terms of native dialogs I would suggest having a look at ACR User Dialogs you can either use the cross-platform library directly or use it for inspiration in creating your own custom implementation.


Explanation

MvvmCross startup life cycle consists of a two-phase (Documentation link).

  1. InitializePrimary - Runs on the main sync context (aka main thread). Initializes the IoC, Logging mechanism and other core parts.
  2. InitializeSecondary - Runs on the background (never on the main thread). Constructs some other platform services like bindings, the App class and calls Initialize on it. It finally registers Views / ViewModels lookups.

The initialized methods calls are as follows (Documentation link)

    // InitializePrimary 
    InitializeFirstChance();                // Earliest possible hook
    InitializeDebugServices();
    InitializePlatformServices();
    InitializeSettings();
    InitializeSingletonCache();

    // InitializeSecondary 
    PerformBootstrapActions();
    InitializeStringToTypeParser();
    InitializeViewModelFramework();
    var pluginManager = InitializePluginFramework();
    InitializeApp(pluginManager);           // App start up trigger here
    InitialiseViewModelTypeFinder();
    InitializeViewsContainer();
    InitiaiseViewDispatcher();
    InitializeViewLookup();
    InitialiseCommandCollectionBuilder();
    InitializeNavigationSerializer();
    InitializeInpcInterception();
    InitializeLastChance();                 // Last possible hook (async process)

You can see here InitializeFirstChance() occurs as the first hook within the InitializePrimary phase while InitializeLastChance() occurs as the last hook in the InitializeSecondaryphase. The issue arises where within InitializeSecondary the method InitializeApp(pluginManager) triggers the startup sequence for the app. This triggers the creation of your first view model which in your current case has a dependency on a service that has not yet been registered against the IoC provider. Leading to the exception showing that it can not resolve the parameter.

InitializeLastChance() usage would be fine if you don't have an immediate requirement for the service. Note, that InitializePlatformServices() is currently marked as deprecated in favour of InitializeFirstChance() (Documenation link).