Improved life cycle support

KentBoogaartKentBoogaart AUMember ✭✭
edited December 2016 in Xamarin.Forms Evolution

Summary

As one of the authors of ReactiveUI, I have found that Xamarin.Forms does not provide the requisite view life cycle hooks to enable the framework to manage the activation and deactivation of resources in response to the comings and goings of views.

Specifically, I have found these problems:

  • Page.Appearing actually fires in response to ViewDidAppear on iOS. One of ReactiveUI's benefits is its code-based, type-safe binding infrastructure. Appearing is the only hook we can use to get stuff on the screen, but it fires too late on iOS, so there is a brief period where the screen is not populated with data. I have already attempted to start a discussion here, but it seemed to stagnate.
  • Given a View, there is no way to tell when that View appears and disappears from the screen. There is no View.Appearing or View.Disappearing events as there is for Page. It might seem appropriate to therefore search up the visual tree for the Page and hook into that. However, this is inefficient (but perhaps that's inevitable) and there is no means of knowing when the Page itself changes. There is no PageChanged event, and PropertyChanged does not fire in response to the Page changing.

API Changes

  • Page.Appearing should fire in response to ViewWillAppear on iOS. Moreover, there should be Page.Appeared and Page.Disappeared counterparts. Yes, this is potentially breaking to people who are hooked into Appearing and rely on it firing in response to ViewDidAppear on iOS. However, that behavior has always been incorrect and the fix for these people is literally to change Appearing to Appeared (or hold off on the XF upgrade).
  • Ideally, add Appearing, Appeared, Disappearing, Disappeared to View and have them Just Work. Failing that, ensure there is a hook by which framework authors can know the hosting Page for a View has changed. Perhaps a specific PageChanged event, or just ensuring PropertyChanged works as expected in this scenario.

Intended Use Case

The use case here is to give framework authors what they need to facilitate the creation of self-sufficient, standalone, encapsulated components by framework consumers. I specifically work on ReactiveUI, but other framework authors will/have run into the same issues.

ModEdit - Spec incoming ASAP

12
12 votes

Accepted · Last Updated

No implementor has been assigned. Let us know in a comment below if you wish to implement this proposal.

Posts

  • TheRealJasonSmithTheRealJasonSmith USXamarin Team Xamurai
    edited December 2016

    I like this. Would you be willing to create a table like so:

    Appearing Appeared Disappearing Disappeared
    iOS ViewWillAppear ViewDidAppear ViewWillDisappear ViewDidDisappear
    Android ... ... ... ...
    UWP ... ... ... ...
  • TheRealJasonSmithTheRealJasonSmith USXamarin Team Xamurai

    Also on further thought I think we would probably need to have a "Compatibility" mode for anyone updating and not wanting to port to the new behavior.

  • AdamPAdamP AUUniversity ✭✭✭✭✭

    A default compatibility mode will be essential. I have never come across any app that doesn't depend on OnAppearing at some point. While it might not cause any issues, it has the potential to cause havoc on existing apps.

  • TheRealJasonSmithTheRealJasonSmith USXamarin Team Xamurai

    Yup, mandatory. Should probably look something like

    Xamarin.Forms.Page.UseLegacyLifecycleEvents = true;
    

    Old apps can just put that in their App.cs to restore the legacy behavior.

  • AdrianKnightAdrianKnight USMember ✭✭✭✭

    Hope you make this a priority because I need to start a new app and I want to do Xamarin.Forms.Page.UseLegacyLifecycleEvents = false; :)

    Having said that, I'm not sure if Android or UWP event cycles are 100% translatable to iOS. Not sure what kind of issues this could create for people.

  • TheRealJasonSmithTheRealJasonSmith USXamarin Team Xamurai

    We really need to make sure we have a good idea of what all the old/new events should be. I think we wrote a spec on this like a year ago but I am afraid we lost the data in the migration to microsoft services...

  • AdamPAdamP AUUniversity ✭✭✭✭✭
    edited December 2016

    This is the best I have. Not everything is 100% translatable. Will require tapping into events on other objects to probably detect an about to disappear. From the Android documentation, OnDetachedToWindow states it no longer has a surface to draw on at the point of OnDetachedToWindow, hence I assume its already gone at this point. The OnGlobalLayout is apparently the only point at which we can detect if Android has finished doing its layout, as you could tap into OnDraw but it can be recursive, so you would never know when it finished.

    |           | Appearing             | Appeared       | Disappearing         | Disappeared | 
    | iOS       | ViewWillAppear        | ViewDidAppear  | ViewWillDisappear    | ViewDidDisappear | 
    | Android   | OnAttachedToWindow    | OnGlobalLayout | ??                   | OnDetachedToWindow | 
    | UWP       | Loading               | OnLoaded       | ??                   | OnUnloaded | 
    
  • TheRealJasonSmithTheRealJasonSmith USXamarin Team Xamurai

    Disappearing is easy enough to emulate on platforms that need it. Basically emit when its removed from the logical hierarchy.

  • KentBoogaartKentBoogaart AUMember ✭✭

    @AdamP my thinking was that if the platform doesn't have a more specific hook, then the same hook resolves to both events. For example, on Android OnAttachedToWindow would fire Appearing and then Appeared immediately after. From the viewpoint of the consumer, I don't think it matters. As long as both events are raised in the right order and they correlate to the best possible hook in the underlying platform.

    I know for a fact that setting up data in Appearing on Android (i.e. in response to OnAttachedToWindow) does not present the same problem as on iOS (data appearing too late).

  • AdamPAdamP AUUniversity ✭✭✭✭✭
    edited December 2016

    @KentBoogaart - if a platform doesn't have a specific hook, I would go down the path of emulating it.

    The problem is if you don't have all platforms behaving exactly same in this scenario it can lead to discrepancies in a XF app, that will be nothing but an absolute pain to then rectify from the app developers view e.g. me :) Say data loading too early, or too late, causing a not so smooth UI experience, or something like that.

  • TheRealJasonSmithTheRealJasonSmith USXamarin Team Xamurai

    Appeared could be handled by a variety of hooks. Draw is a bad one since overriding draw has performance implications, but the layout hook might work. We could even go as stupid as a timer if we want to be as loose as saying "Appeared will happen some time shortly after the object is on screen but not always immediately so"

  • KentBoogaartKentBoogaart AUMember ✭✭

    @AdamP As someone who also develops apps for a day job, I'm struggling to understand why emulation would lead to fewer headaches for me as an app developer. I don't think it's realistic to have exact same behavior across all platforms, but if we were trying to get as close to that as possible then we could just change iOS to use ViewWillAppear instead of ViewDidAppear.

    That said, if a dependable emulation can be found I'm all for it. What worries me is trying to get too clever and creating more problems than we started with. I can imagine the underlying platform evolving and breaking the emulation, perhaps with no recourse. If we're only using life cycle hooks that are officially supported by the platform, that feels a lot safer to me.

  • huangjinshehuangjinshe USMember ✭✭✭
    edited December 2016

    Too complex for leave 4 events. I think currently 2 events good enough already. could we let things more simple and easy for now?(for UWP especially)

  • JeremyKolbJeremyKolb USMember ✭✭✭
    edited December 2016

    @huangjinshe We definitely need more than two events. I think @TheRealJasonSmith summed up the scenarios pretty well.

    ModEdit - In the future please attempt to provide more than just a contrary/supportive opinion to help keep the conversation on track and productive :) Your rational as to why we need more than two would be a great addition to this post. If you simply agree with a previous post push the "Like" button. I am going to leave the comment in place so others may learn.

  • rmarinhormarinho PTMember, Insider, Beta Xamurai

    I agree with the events, one of the thing to take notice is the hierarchy the events fire, so if i m child of a NavPage, nav page should fire first, then the child page. This is more complex that it seems in order to be consistent between platforms.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭
    edited December 2016

    @huangjinshe
    You can use the events, but you don't have to use it.
    For me, this is a real problem and a good proposal (that should be accepted)

    ModEdit - Removed strong formatting.

  • ChaseFlorellChaseFlorell CAInsider, University mod

    Continuing on the lifecycle conversation. I think there's a strong need for one (possibly two) more beyond what's already accepted.

    I'd like to propose at minimum OnRemoved and possibly OnRemoving (but maybe not necessary). The reason for this is for cleanup when a Page is being removed from the navigation hierarchy.

    I've created a separate proposal, but it really does belong here.
    https://forums.xamarin.com/discussion/85745/add-onpopped-method-to-page

    Intended Use Case

    The biggest reason for this is to solve the issue whereby we need to clear Behaviors from a VisualElement, especially when those behaviors are added via xaml. If we tap into the existing methods (even the ones proposed above), we would be cleaning up our page when a new page is pushed over top and not limited to when the page is popped.

    Example:

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MyFunPage
    {
        public MyFunPage()
        {
            InitializeComponent();
        }
    
        protected override void OnRemoved()
        {
            // clean up behaviors once the view is definitely gone.
            MyVisualElement.Behaviors.Clear();
            OtherVisualElement.Behaviors.Clear();
        }
    
        // note: a destructor/finalizer doesn't solve this because it doesn't get called the way one would hope
    }
    
  • DanielLDanielL PLInsider ✭✭✭✭

    Is it possible to also unify NavigationStack behaviour while making this change?

    Last time I checked (some time ago), the behaviour on Android was:

    • Push a page to NavigationStack, then Pop it. Result: it isn't in NavigationStack anymore
    • Push a page to NavigationStack, then Tap navigation back button. Result: it's still in NavigationStack, until another page is pushed.
  • ToniPetrinaToniPetrina HRUniversity ✭✭

    That makes sense because one might go forward in time when going back. Like browsers do today.
    However, when you pop a view, it is closed forever - just like a modal dialog.

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭
    edited January 18

    @TheRealJasonSmith said:
    First, I want to post a reminder that "+1" style comments are not allowed. There is a like button if you agree with something.

    Please bring back that Like button! It's gone in the Evolution forum.
    (now I have a Like button on my own post, but not on others')

    I agree with @ChaseFlorell that we need to distinguish between when a page was popped or another has been pushed over it. In the former case you need to clean up, in the latter not.

    I also opened a bug a few years back that Appearing/Disappearing were raised in a different order on iOS and Android when you pushed/popped a page. IMHO it is very important that the order is consistent over platforms. Unfortunately the bug got closed (not fixed) and I can't search for closed bugs on bugzilla.

  • Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭
    edited January 26

    @MichaelRumpler said:

    @TheRealJasonSmith said:
    First, I want to post a reminder that "+1" style comments are not allowed. There is a like button if you agree with something.

    Please bring back that Like button! It's gone in the Evolution forum.
    (now I have a Like button on my own post, but not on others')

    I agree with @ChaseFlorell that we need to distinguish between when a page was popped or another has been pushed over it. In the former case you need to clean up, in the latter not.

    I also opened a bug a few years back that Appearing/Disappearing were raised in a different order on iOS and Android when you pushed/popped a page. IMHO it is very important that the order is consistent over platforms. Unfortunately the bug got closed (not fixed) and I can't search for closed bugs on bugzilla.

    +1

    In all seriousness, in case it's not this way for others, the only place I do see the like button is on my own posts

  • PierceBogganPierceBoggan USForum Administrator, Xamarin Team, Developer Group Leader Xamurai
    edited January 26

    @Joshua.R.Russo said:

    @MichaelRumpler said:

    @TheRealJasonSmith said:
    First, I want to post a reminder that "+1" style comments are not allowed. There is a like button if you agree with something.

    Please bring back that Like button! It's gone in the Evolution forum.
    (now I have a Like button on my own post, but not on others')

    I agree with @ChaseFlorell that we need to distinguish between when a page was popped or another has been pushed over it. In the former case you need to clean up, in the latter not.

    I also opened a bug a few years back that Appearing/Disappearing were raised in a different order on iOS and Android when you pushed/popped a page. IMHO it is very important that the order is consistent over platforms. Unfortunately the bug got closed (not fixed) and I can't search for closed bugs on bugzilla.

    +1

    In all seriousness, in case it's not this way for others, the only place I do see the like button is on my own posts

    We're working on fixing it. :smile: It's a Vanilla Forums issue.

    /backontopic

  • rmarinhormarinho PTMember, Insider, Beta Xamurai

    @DanielL i think it depends where we check, the NavigationStack can be update after the animation finished vs being updated before, but yes we can fix that i just also found this issue while working on MacOS branch.

  • DanielLDanielL PLInsider ✭✭✭✭

    @rmarinho :+1: Fixing that bug would be very useful for me, eg. I could implement navigation back event.

    i think it depends where we check, the NavigationStack can be update after the animation finished vs being updated before

    On Android when navigation is used (back icon), it isn't removed from navigation stack at all. It's removed only if another page is pushed.

  • MelbourneDeveloperMelbourneDeveloper AUMember ✭✭✭

    Please fix this.

  • KentBoogaartKentBoogaart AUMember ✭✭

    Note that I've posted an interim, hacky workaround for the iOS ViewDidAppear problem here: http://kent-boogaart.com/blog/hacking-xamarin.forms-page.appearing-for-ios

  • trung.dinhtrung.dinh VNMember ✭✭

    that's awesome! @KentBoogaart

  • BoguslawBlonskiBoguslawBlonski PLMember ✭✭✭
    edited March 27

    How's the progress ? :)
    Just for info please note that people are coming to xamarin from other technologies WPF, WinForms, WebForms, Js ... Web , it would be nice to have same names, page loaded loaded ..

  • SabdullahSabdullah USMember ✭✭

    We really need a few other hooks, Create, Created, Destroyed, Destroying.

    This may look like the Android Activity lifecycle.

    Or the Android Fragment lifecycle:

    And the Disappearing or Destroyed (or Destroying) hook should also allow the app to save the view's state (preferably a page's state) in a dictionary like object (Android's bundle concept) and that same object should be given back to the view's resurrected object if the app is being restarted.

    But for all of this to work, Forms needs to take control of the app's lifecycle and retain and restore Page / View states and navigation stacks upon application restarts. Unless this is implemented, almost none of the lifecycle hooks give us much advantage...

  • ChrisBoydChrisBoyd USMember ✭✭

    I feel like it might be important to note that Google has looked into issues with lifecycle management on Android and came up with this for their new Architecture components.

    They even provide new ways to track the lifecycle state and, thus, allow you to determine whether or not a specific function should run at this time.

    e.g.:

        public void enable() {
            enabled = true;
            if (lifecycle.getState().isAtLeast(STARTED)) {
                // connect if not connected
            }
        }
    
  • WilliamJockuschWilliamJockusch USMember ✭✭

    I think the most important thing is to have every lifecycle event from every platform fire some callback in the page. I think it's fine if, as a consequence, there are some Page callbacks events that never fire on some platforms. It would be important to document that, obviously, maybe with a table of what platform event corresponds to which Page event.

Sign In or Register to comment.