xamarinmvvmxamarin.iosmvvmcross

Xamarin iOS can't launch first view controller without a storyboard and using MVVMCross 7.0.0


After I updated mvvmcross to v7.0.0 in my xamarin ios app, I can't get it running. It just freezes in the splash screen and doesn't launch the first view controller. I checked AppDelegate and Setup files, and everything seems fine. The view controller constructor is hit, also the view model, but the ViewDidLoad method in the view controller isn't.

 [Register("AppDelegate")]
    [assembly: Preserve(typeof(MvxIosSetup<App>))]
    public partial class AppDelegate : MvxApplicationDelegate<MvxIosSetup<App>, App>
    {
        /// <summary>
        /// UIApplicationDelegate.Window doesn't really exist / work. It was added by Xamarin.iOS templates 
        /// </summary>
        public new virtual UIWindow Window { get; set; }

        public AppDelegate() : base()
        {
            RegisterSetup();
        }

        public override void WillEnterForeground(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.ActivatedFromMemory);
        }

        public override void DidEnterBackground(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.Deactivated);
        }

        public override void WillTerminate(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.Closing);
        }

        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {
            Window = new UIWindow(UIScreen.MainScreen.Bounds);

            var setup = new Setup();
            setup.InitializePrimary();
            setup.InitializeSecondary();
            setup.PlatformInitialize(this, Window);

            var myFirstViewController = new LoginView();
            Window.RootViewController = myFirstViewController;

            var startup = Mvx.IoCProvider.Resolve<IMvxAppStart>();
            startup.Start();

            Window.MakeKeyAndVisible();

                   

            SetupUiStyles();

            return true;
        }

        private void FireLifetimeChanged(MvxLifetimeEvent which)
        {
            var handler = LifetimeChanged;
            handler?.Invoke(this, new MvxLifetimeEventArgs(which));
        }

        public event EventHandler<MvxLifetimeEventArgs> LifetimeChanged;

        private void SetupUiStyles()
        {
            UINavigationBar.Appearance.TintColor = UIColor.Black;
            UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.Default;

            if (UIDevice.CurrentDevice.CheckSystemVersion(15, 0))
            {
                var appearance = new UINavigationBarAppearance()
                {
                    BackgroundColor = UIColor.FromRGB(99, 183, 105),
                    TitleTextAttributes = new UIStringAttributes() { ForegroundColor = UIColor.White }
                };



                UINavigationBar.Appearance.StandardAppearance = appearance;
                UINavigationBar.Appearance.CompactAppearance = appearance;
                UINavigationBar.Appearance.ScrollEdgeAppearance = appearance;
            }

            // Disable check for delegate in event registration: https://forums.xamarin.com/discussion/39470/installed-5-9-build-431-event-registration-is-overwriting-existing-delegate-error
            UIApplication.CheckForEventAndDelegateMismatches = false;
        }
       
    }

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

        }

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

        protected override IMvxIosViewPresenter CreateViewPresenter()
        {
            return new MvxIosViewPresenter(ApplicationDelegate, Window);
        }

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

        public override void InitializeSecondary()
        {
            Mvx.IoCProvider.RegisterType<LoginViewModel, LoginViewModel>();
            InitializeViewModelTypeFinder();
            base.InitializeSecondary();
        }


        protected override IMvxNameMapping InitializeViewModelTypeFinder()
        {
            var viewModelByNameRegistry = CreateViewModelByNameRegistry();

            var viewModelAssemblies = GetViewModelAssemblies();

            foreach(var assemly in viewModelAssemblies)
            {
                viewModelByNameRegistry.AddAll(assemly);
            }

            var nameMapping = CreateViewToViewModelNaming();
            Mvx.IoCProvider.RegisterSingleton(nameMapping);

            return nameMapping;
        }

    }


 public class BaseView<T> : MvxViewController where T : MvxViewModel
    {
        protected BaseView(string name, NSBundle bundle = null)
        {

        }

        public new T ViewModel
        {
            get
            {
                return base.ViewModel as T;
            }
            set
            {
                base.ViewModel = value;
            }

        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
        }
}
 [MvxRootPresentation]
    public partial class LoginView : BaseView<LoginViewModel>, IBaseView
    {
        public LoginView()
            : base("LoginView", null)
        {
           // being hit
        }

       public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            //not being hit
        }
    }

I tried the code above and other stuff. Sometimes the ViewModel is not binded to View (DataContext null) other time set.Apply() gets an error. But when it works, LoginView doesn't show and the app freezes on splash screen


Solution

  • Thanks Cheesebaron. I figured out what was wrong before reading your comment, but your are right.

    This was my final FinishedLaunching method, with no Window.MakeKeyAndVisible() or

    var startup = Mvx.IoCProvider.Resolve();

    startup.Start();

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
            {
                Window = new UIWindow(UIScreen.MainScreen.Bounds);
    
                Window.RootViewController = new UINavigationController();
    
                var result = base.FinishedLaunching(application, launchOptions);
    
                SetupUiStyles();
    
                return result;
            }
    
      public class Setup : MvxIosSetup<App>
        {
            protected override void InitializeFirstChance()
            {
                base.InitializeFirstChance();
    
                Mvx.IoCProvider.RegisterType<IMvxJsonConverter, MvxJsonConverter>();
            }
    
            protected override void InitializeLastChance()
            {
                base.InitializeLastChance();
            }
    
            protected override IMvxIocOptions CreateIocOptions()
            {
                return new MvxIocOptions
                {
                    PropertyInjectorOptions = MvxPropertyInjectorOptions.MvxInject
                };
            }
        }
    
    

    The setup file from MVVMCross 6.4.x and beyond (6.4.2, 7.0.0 - I tested), doesn't need CreateApp method

    And my base ViewController and ViewControllers, there were no need to have a constructor, since I was not using storyboard

        public class BaseView<T> : MvxViewController<T> where T : MvxViewModel
        {
        //no constructor 
        }
    
        [MvxRootPresentation]
        public partial class LoginView : BaseView<LoginViewModel>, IBaseView
        {
           public LoginView()
           {
           //no base reference  like before
           }
        }
    

    And at last, I removed some NavigatorController from LoginView, where we used to set some propertities