Skip to content
This repository has been archived by the owner on Dec 18, 2017. It is now read-only.

iOS Start/Sleep/Resume not being called #41

Open
NightOwlCoder opened this issue Feb 1, 2016 · 14 comments
Open

iOS Start/Sleep/Resume not being called #41

NightOwlCoder opened this issue Feb 1, 2016 · 14 comments

Comments

@NightOwlCoder
Copy link

Hey there,

I've code like this on MvxIosSetup:

        protected override IMvxIosViewPresenter CreatePresenter()
        {
            Forms.Init();

            var mvxFormsApp = new MvxFormsApp();

            mvxFormsApp.Start += (s, e) => { StartMessage.Publish(); };
            mvxFormsApp.Sleep += (s, e) => { SleepMessage.Publish(); };
            mvxFormsApp.Resume += (s, e) => { ResumeMessage.Publish(); };

            return new MvxFormsIosPagePresenter(Window, mvxFormsApp);
        }

But those methods are never called.

Is this a known issue or am I doing something wrong?

@NightOwlCoder
Copy link
Author

I get the same situation when running UWP on a WP emulator.

Droid is the only one that is actually working fine.

I've also asked this question on SO:
http://stackoverflow.com/questions/35142319/xamarin-forms-start-sleep-resume-not-being-called-when-using-mvvmcross-forms

@Cheesebaron
Copy link
Member

@codeknox
Copy link

codeknox commented Feb 2, 2016

Brand new solution on VS does the expected:

using Xamarin.Forms;
using System.Diagnostics;
namespace XS.Forms
{
    public class App : Application
    {
        public App()
        {
            MainPage = new ContentPage
            {
                Content = new StackLayout
                {
                    VerticalOptions = LayoutOptions.Center,
                    Children = { new Label { XAlign = TextAlignment.Center, Text = "Welcome to Xamarin Forms!" } }
                }
            };
        }

        protected override void OnStart()
        {
            Debug.WriteLine("start");
        }

        protected override void OnSleep()
        {
            Debug.WriteLine("sleep");
        }

        protected override void OnResume()
        {
            Debug.WriteLine("resume");
        }
    }
}

Console output for iOS run:

Launching 'XSFormsiOS' on 'iPhone 6s Plus iOS 9.2'...
Thread started:  #2
Thread started:  #3
2016-02-02 09:45:17.895 XSFormsiOS[82830:5257324] start
2016-02-02 09:45:32.227 XSFormsiOS[82830:5257324] sleep[0:] sleep
2016-02-02 09:45:37.095 XSFormsiOS[82830:5257324] resume[0:] resume
2016-02-02 09:45:39.410 XSFormsiOS[82830:5257324] sleep[0:] sleep
2016-02-02 09:45:40.799 XSFormsiOS[82830:5257324] resume
The app has been terminated.

and Console output for Droid run:

Android application is debugging.
InspectorDebugSession(0): HandleTargetEvent: TargetReady
Thread started:  #3
02-02 09:39:51.988 I/mono-stdout( 2015): start
02-02 09:39:52.183 I/mono-stdout( 2015): sleep
02-02 09:40:16.844 I/mono-stdout( 2015): resume
02-02 09:40:24.163 I/mono-stdout( 2015): sleep
02-02 09:40:35.610 I/mono-stdout( 2015): resume
InspectorDebugSession(0): HandleTargetEvent: ThreadStopped
Thread started: <Thread Pool> #7
InspectorDebugSession(0): HandleTargetEvent: ThreadStarted
InspectorDebugSession(0): Disposed

I switched the app a few times to BG and back.

@Cheesebaron
Copy link
Member

Are you sure that your mvxFormsApp isn't GC'ed? Try creating it as a member variable.

@codeknox
Copy link

codeknox commented Feb 2, 2016

As it goes as a parameter to the presenter and it saves it, I did not think it was necessary.

new MvxFormsIosPagePresenter(Window, mvxFormsApp);

Anyway, I created a local fiend but unfortunately, got the same end result.

@codeknox
Copy link

codeknox commented Feb 2, 2016

So I got the source code for the presenter, and added the Example002XAML PCL/iOS into it, removed packages from it and pointed to src code on the solution.
Created a default ctor on MvxFormsApp and put a BP there and on the 3 overrides.

Unfortunately, only the ctor one is hit, the overrides never came.

But as I already tested a pure Form app and they are calling this, looks like we are doing something out of order?

Any other idea that can help me move further in the investigation?

@codeknox
Copy link

codeknox commented Feb 2, 2016

Something else I just found, there is no LoadApplication in iOS code.
Isn't it necessary like on the other platforms?

@codeknox
Copy link

codeknox commented Feb 2, 2016

Probably exactly what is missing?

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

Above code is from a pure Xamarin Forms iOS project.
But unfortunately LoadApplication is only available if we inherit from Xamarin.Forms.Platform.iOS.FormsApplicationDelegate.

Looks like we need some refator on the base class here?

@helmerm
Copy link

helmerm commented Mar 21, 2016

Is there any update on this one? Is there a workaround?

This probably also causes the Start/ReloadState/SaveState to be not called on iOS.

@xabre
Copy link

xabre commented Mar 21, 2016

I solved this in my current project by doing the following:

  • I wrote my implementation of the app delegate which extends FormsAppDelegate and implements IMvxApplicationDelegate:
public class MyApplicationDelegate : FormsApplicationDelegate, IMvxApplicationDelegate
      {
        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 void FinishedLaunching(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.Launching);
        }

        private void FireLifetimeChanged(MvxLifetimeEvent which)
        {
            var handler = LifetimeChanged;
            if (handler != null)
                handler(this, new MvxLifetimeEventArgs(which));
        }

        #region IMvxLifetime implementation

        public event EventHandler<MvxLifetimeEventArgs> LifetimeChanged;

        #endregion
    }
  • The AppDelegate now inherits from MyApplicationDelegate and in the FinishedLaunching override I do the following:
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            Forms.Init();
            Forms.ViewInitialized += (sender, e) =>
            {
                if (!string.IsNullOrWhiteSpace(e.View.StyleId))
                {
                    e.NativeView.AccessibilityIdentifier = e.View.StyleId;
                }
            };
            var formsApp = new MyFormsApp();

            var setup = new Setup(this, formsApp);
            setup.Initialize();

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

            LoadApplication(formsApp);

            return base.FinishedLaunching(app, options);
        }
  • You probably noticed that I don't use an explicit window and set it to visible in the AppDelegate anymore. This is because it's handled by LoapApplication anyway and does not need to be done explicitly. Also the view presenter does not need any native code anymore like (for example CustomPlatformInitialization) because setting formaApp.MainPage and then pushing and popping navigation pages is enough.

Basicly the solution consists in using LoadApplication and removing the explicit window.

@helmerm
Copy link

helmerm commented Mar 21, 2016

Thanks! How does your Setup and FormsApp class look like? Do I need to initialize the MainPage in the FormsApp class? What did you pass to the MvxIosSetup base class constructor?

@xabre
Copy link

xabre commented Mar 21, 2016

My setup looks like this:

  public class Setup : MvxIosSetup
    {
        private readonly Xamarin.Forms.Application _app;

        public Setup(IMvxApplicationDelegate applicationDelegate, Xamarin.Forms.Application app)
            : base(applicationDelegate, window: null)
        {
            _app = app;
        }

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

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

        protected override IMvxIosViewPresenter CreatePresenter()
        {
            var presenter = new MyIosFormsViewPresenter(_app);
            return presenter;
        }
}

MyFormsApp (inside the PCL) is just:

 public class MyFormsApp : MvxFormsApp
    {

        protected override void OnStart()
        {
            // Handle when your app starts
            Mvx.Trace("App start");
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
            Mvx.Trace("App sleep");
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
            Mvx.Trace("App resume");
       }
}

My custom presenter is something like (the window reference is not needed):

public class MyIosFormsViewPresenter : MvxFormsPagePresenter, IMvxIosViewPresenter
    {
        public MyIosFormsViewPresenter(Xamarin.Forms.Application mvxFormsApp)
            : base(mvxFormsApp)
        {

        }

        public bool PresentModalViewController(UIViewController controller, bool animated)
        {
            return false;
        }

        public void NativeModalViewControllerDisappearedOnItsOwn()
        {

        }
    }

@helmerm
Copy link

helmerm commented Mar 21, 2016

Thanks!

Do the application lifecycle events work correctly for you?

On android, the OnResume method on the FormsApplication does not get called, when bringing the app to foreground, but the OnStart method is called instead.

On iOS the OnResume method on the FormsApplication works, but the OnStart method is not invoked on the ViewModel on resume.

The SaveStateToBundle and ReloadFromBundle methods on my view model are never executed, neither on android nor iOS.

@xabre
Copy link

xabre commented Mar 21, 2016

These application lifecyle methos have nothing to do with the view models direclty (or at least out of the box). They work fine for my app.

The Start method of the MvxViewModel is called after the view model is instantiated by the ViewPresenter.

I had to forward these events to my view model manually. For example, for 'App -> OnResume' I get the topmost view (Page), then I get the binding context and if the binding context is my BaseViewModel then I call OnResume on it.

I can't tell you more about SaveStateToBundle and ReloadFromBundle because I'm not using them in my forms app but I guess they also need to be triggered.

Moreover, I defined methods like OnSuspended, OnResumed, OnLoaded etc in my BaseViewModel and trigger them from a BasePage by overriding Page methods like OnAppearing, OnDissapearing, OnBindingContextChanged, OnBackButtonPressed. This way my ViewModels are aware of the state of their views.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

5 participants