MvvmCross App Won't Start after upgrading to version 6.0
Asked Answered
U

4

6

I upgraded my app to MvvmCross version 6.0. Now it get's to the splash screen and does nothing else. I can see the the Services are starting up by looking at the console. Here is my app.cs:

public class App : MvxApplication
{
    public override void Initialize()
    {
        MPS.ApplicationName = Settings.ApplicationName;
        EventLog.ApplicationName = Settings.ApplicationName;
        BlobCache.ApplicationName = Settings.ApplicationName;

        CreatableTypes()
            .EndingWith("Service")
            .AsInterfaces()
            .RegisterAsLazySingleton();

        CreatableTypes()
            .EndingWith("Singleton")
            .AsInterfaces()
            .RegisterAsSingleton();

        //RegisterAppStart(new CustomAppStart());
        //RegisterNavigationServiceAppStart<LoginViewModel>();
        RegisterAppStart<LoginViewModel>();
    }

}

It is pretty basic. I had converted to the new navigation system hence the RegisterNavigationServiceAppStart. That would no longer resolve so I went back to a straight RegisterAppStart. The splash screen comes up and then it stops. In case it matters, splashscreen.cs is as follows:

[Activity(
    Label = "@string/ApplicationName"
    , MainLauncher = true
    , Icon = "@drawable/icon"
    , Theme = "@style/Theme.Splash"
    , NoHistory = true)]
    //, ScreenOrientation = ScreenOrientation.Landscape)]
public class SplashScreen : MvxSplashScreenActivity
{
    public SplashScreen()
        : base(Resource.Layout.SplashScreen)
    {
    }
}

It's pretty vanilla, but I know things have changed along the way. My setup.cs is as follows:

public class Setup : MvxAndroidSetup
{
    //public Setup(Context applicationContext)
    //    : base(applicationContext)
    //{
    //}

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

    //protected override IMvxTrace CreateDebugTrace()
    //{
    //    return new DebugTrace();
    //}

    protected override IMvxAndroidViewPresenter CreateViewPresenter()
    {
        return new MvxAppCompatViewPresenter(AndroidViewAssemblies);
    }

    protected override void FillValueConverters(IMvxValueConverterRegistry registry)
    {
        base.FillValueConverters(registry);
        registry.AddOrOverwrite("DateToStringConverter", new DateToStringConverter());
        registry.AddOrOverwrite("FloatToStringConverter", new FloatToStringConverter());
        registry.AddOrOverwrite("DecimalToStringConverter", new DecimalToStringConverter());
        registry.AddOrOverwrite("BoolToViewStatesConverter", new BoolToViewStatesValueConverter());
        registry.AddOrOverwrite("ShipmentToOriginConverter", new ShipmentToOriginConverter());
        registry.AddOrOverwrite("ShipmentToDestinationConverter", new ShipmentToDestinationConverter());
        //registry.AddOrOverwrite("CustomName2", new AnotherVerySpecialValueConverter("Summer"));
    }

    protected override void FillTargetFactories(MvvmCross.Binding.Bindings.Target.Construction.IMvxTargetBindingFactoryRegistry registry)
    {
        base.FillTargetFactories(registry);
        registry.RegisterCustomBindingFactory<EditText>("FocusText",
                                                  textView => new MvxEditTextFocusBinding(textView));
        registry.RegisterCustomBindingFactory<TextView>("FocusChange",
                                                  textView => new MvxTextViewFocusChangeBinding(textView));
        //registry.RegisterCustomBindingFactory<MvxSpinner>("ItemSelected",
        //                                          spinner => new MvxSpinnerItemSelectedBinding(spinner));
    }

    protected override IEnumerable<Assembly> AndroidViewAssemblies => new List<Assembly>(base.AndroidViewAssemblies)
    {
        typeof(MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView).Assembly
    };

}

The only change I made to it was to remove the constructor. Per the documentation for version 6.0, the constructor no longer has parameters so I saw no reason to call it. Can anyone help?

** Update **

I added a MainApplication.cs as follows:

[Application]
public class MainApplication : MvxAppCompatApplication<Setup, App>
{
    public MainApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
    {
    }
}

This got me past the splash screen, but hung up on the Initialize of the LoginViewModel.

* Logs *

Maybe this will help. Here are the event log entries:

2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Primary start
2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: FirstChance start
2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: PlatformServices start
2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: MvvmCross settings start
2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Singleton Cache start
2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: ViewDispatcher start
2018-04-17 12:17:06 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Bootstrap actions
2018-04-17 12:17:07 [TRACE] (MvvmCross.Logging.MvxLog) No view model type finder available - assuming we are looking for a splash screen - returning null
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: StringToTypeParser start
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: CommandHelper start
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: PluginManagerFramework start
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Create App
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: NavigationService
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Load navigation routes
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: App start
2018-04-17 12:17:07 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Application Initialize - On background thread
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: ViewModelTypeFinder start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: ViewsContainer start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Views start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: CommandCollectionBuilder start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: NavigationSerializer start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: InpcInterception start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: InpcInterception start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: LastChance start
2018-04-17 12:17:08 [TRACE] (MvvmCross.Core.MvxSetup) Setup: Secondary end
2018-04-17 12:17:08 [TRACE] (MvvmCross.Logging.MvxLog) AppStart: Application Startup - On UI thread

I traced it a little further. It is hanging up on the Initialize() in the view model. I created a test to demonstrate:

public class FirstViewModel : MvxViewModel
{

    public FirstViewModel()
    {
        Task.Run(async () =>
        {
            var l = await ListDataSource.GetLocations();
            var m = l;
        });
    }

    public async override Task Initialize()
    {
        await base.Initialize();
        var l = await ListDataSource.GetLocations();
        var m = l;
    }

}

If I set a break point on the two var m = l, It will get to the one in the constructor but will never get to the one in the Initialize. GetLocations is:

    public async static Task<LocationList> GetLocations()
    {
        ListServiceClient client = NewClient();

        LocationList ret = null;
        bool TryCache = false;

        try
        {
            //ret = await client.GetLocationListAsync();
            ret = await Task<LocationList>.Factory.FromAsync((asyncCallback, asyncState) => client.BeginGetLocationList(asyncCallback, asyncState),
               (asyncResult) => client.EndGetLocationList(asyncResult), null);

            client.Close();
            await BlobCache.LocalMachine.InsertObject("Locations", ret, DateTimeOffset.Now.AddDays(Settings.CacheDays));
        }
        catch (TimeoutException ex)
        {
            client.Abort();
            EventLog.Error(ex.ToString());
            TryCache = true;
        }
        catch (CommunicationException ex)
        {
            client.Abort();
            EventLog.Error(ex.ToString());
            TryCache = true;
        }
        catch (Exception ex)
        {
            EventLog.Error(ex.ToString());
            TryCache = true;
        }

        if (TryCache)
        {
            try
            {
                ret = await BlobCache.LocalMachine.GetObject<LocationList>("Locations");
            }
            catch (KeyNotFoundException)
            {
                ret = null;
            }
        }

        return ret;
    }

If you set a break point on Client.Close(), it will get there if called from the constructor but not if its called from Initialize.

Unmixed answered 16/4, 2018 at 19:40 Comment(8)
Can you do me a favour, can you put a try catch around your RegisterAppStart<LoginViewModel>(); and check if the catch block is hit? Additionally, not related to your issue (I think) I would suggest using MvxAppCompatSetup over MvxAndroidSetup then you do not need to override CreateViewPresenter as it uses the compat presenter by default.Jabot
I did as you said and changed the Setup to MvxAppCompatSetup. I also changed the splash screen to MvxSplashScreenAppCompatActivity while I was at it. This did not change the behavior. I didn't get an error from the RegisterAppStart. I went a little further and added a MainApplication as shown above. This got me to the Initialize of the ViewModel but no further.Unmixed
It used to be that the OnCreate event in the View started up the ViewModel logic. It never gets to the OnCreate in the View but the ViewModel is definitely instantiated.Unmixed
@JimWilcox You might want to have a look at the step by step blog post guide, by Nick Randolph, on setting up Xamarin projects with latest version of MvvmCross. He's got a github repo for the sample as well. I know it's an update on your end but maybe check if you haven't missed anything that has been mentioned in the blog postTwill
This project has been working fine with version 5.X. I had even updated everything to the new navigation manager. I had not used a MainApplication.cs before today. It wasn't necessary in version 5.X. Is there a guide somewhere that tells you how to migrate from 5.x to 6.0? I have read whats out there but it mentions things like app start and the like that I never used. I always kept it as vanilla as possible.Unmixed
Yes there's an official guide, although I've never found them very informativeTwill
Yeah I read that one, but it was talking about changes to features that I didn't use anyway. My app only supports Android so I never had to worry about App Start because I had a main launcher activity.Unmixed
This is definitely a bug. I created a repo and posted the bug github.com/MvvmCross/MvvmCross/issues/2808Unmixed
U
7

My problem was twofold. First, when you upgrade to version 6.0 using Android, you now have to include a MainApplication.cs as follows:

[Application]
public class MainApplication : MvxAppCompatApplication<Setup, App>
{
    public MainApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
    {
    }
}

Without this, you will get stuck on the SplashScreen. Secondly, you need to know that the Initialize in the first ViewModel that you display has to be synchronous. As @Ale_lipa mentioned, an MvvmCross Author wrote a blog post explaining why this is and what to do about it.

https://nicksnettravels.builttoroam.com/post/2018/04/19/MvvmCross-Initialize-method-on-the-first-view-model.aspx

In a nutshell, if you are using a SplashScreen and you really need the Initialize for your first ViewModel to be Async, you can add a CustomAppStart as follows:

public class CustomMvxAppStart<TViewModel> : MvxAppStart<TViewModel>
     where TViewModel : IMvxViewModel
{
     public CustomMvxAppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService)
     {
     }

    protected override void NavigateToFirstViewModel(object hint)
     {
         NavigationService.Navigate<TViewModel>();
     }
}

In you App.cs, replace your:

RegisterAppStart<FirstViewModel>();

with:

RegisterCustomAppStart<CustomMvxAppStart<FirstViewModel>>();

This will allow your first Initialize to be async. I am only sure it works for Android and only if you are using a SplashScreen.

Unmixed answered 20/4, 2018 at 20:17 Comment(1)
I can't say how much I love you. Thank you!Education
S
1

I am quite clear, that this is not the issue related to the code from this question, but it's directly related to upgrade to MvvmCross 6.0 I've bumped into.

Just adding this as it might help someone to save a few hours as this thing not mentioned in official documentation reg. upgrading as of now.

So, in my case it was an issue with overloaded methods in Setup class:

    public override IEnumerable<Assembly> GetViewAssemblies()
    {
        var list = new List<Assembly>();
        list.AddRange(base.GetViewAssemblies());
        list.Add(typeof(App).GetTypeInfo().Assembly);

        return list;
    }

    public override IEnumerable<Assembly> GetViewModelAssemblies()
    {
        var list = new List<Assembly>();
        list.AddRange(base.GetViewModelAssemblies());
        list.Add(typeof(CoreApp).GetTypeInfo().Assembly);

        return list;
    }

I needed that as my Views and ViewModel are located in separate projects. After the upgrade the app wasn't running further than initialization, without any errors. After removing those everything started working fine. (Apparently, the newest version handles that automatically).

Signification answered 7/6, 2018 at 17:35 Comment(0)
T
0

For everyone benefit, Jim created a bug in MvvmCross GitHub repo. One of the MvvmCross project contributors provided a workaround for his problem and blogged about it.

Twill answered 19/4, 2018 at 10:21 Comment(0)
S
0

Further to Jim's post, I have a SplashScreen and my startup view model depends on whether this is a first activation or not and the decion about it is made in Core project's AppStart.Startup() method.

My app seems to be working right now and at a high level, the key classes look like this:

Core.App

public class App : MvxApplication
{           
    public override void Initialize()
    {
        // IoC registrations here

        RegisterAppStart(new AppStart(this));            
    }   
}

Core.AppStart

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

    protected override async void Startup(object hint = null)
    {
        base.Startup(hint);

        // logic to navigate to intro viewmodel or home viewmodel
    }
}

Droid.Setup

public class Setup : MvxAppCompatSetup<App>
{                
    protected override IMvxApplication CreateApp()
    {
        return new Core.App();
    }

    protected override void InitializeIoC()
    {
        base.InitializeIoC();
        // Custom IoC initialization
    }

    protected override IMvxAndroidViewPresenter CreateViewPresenter()
    {            
        return new MvxAppCompatViewPresenter(AndroidViewAssemblies);
    }

    protected override IEnumerable<Assembly> AndroidViewAssemblies => new List<Assembly>(base.AndroidViewAssemblies)
    {
        typeof(NavigationView).Assembly,
        typeof(FloatingActionButton).Assembly,
        typeof(Android.Support.V7.Widget.Toolbar).Assembly,
        typeof(Android.Support.V4.Widget.DrawerLayout).Assembly,
        typeof(Android.Support.V4.View.ViewPager).Assembly,
    };

    protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
    {
        MvxAppCompatSetupHelper.FillTargetFactories(registry);
        base.FillTargetFactories(registry);
    }
}
Strawworm answered 21/4, 2018 at 0:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.