Using HttpClient before setting the MainPage

When my Xamarin.Forms app starts, I need to determine if the bearer-token stored locally is still valid.
Therefore I need to send a webrequest (I'm using the HttpClient in a RestService).

Step one of the application is to check if the bearer token stored in memory is still valid.
Therefore I need to send a webrequest before doing anything else.

Step two is to set the MainPage to either the HomePage or the LoginPage (depending on wether the token is still valid).

The problem is that I'm getting a NullReferenceException when sending a webrequest before setting the MainPage.
I've stripped the code down to the bare minimum. The code is hosted at: https://github.com/PieterjanDeClippel/Xamarin.Forms-HttpClient-Issue

What is the reason I'm getting a NullReferenceException? What does it mean? Why isn't such a simple piece of code working as expected?

Posts

  • When testing, please make sure to try and uncomment the error directive at the top of the App.xaml.cs

  • AndreiMisiukevich_AndreiMisiukevich_ USMember ✭✭✭✭

    App can't start without MainPage
    You must set any page before (SplashPage or just empty ContentPage)

  • In either case the MainPage is set before the LoadApplication command is called.

  • MikilllMikilll Member ✭✭✭

    I encountered this issue also. Generally, the error is thrown when you are doing some asynchronous stuff before setting the main page. In such a case, an app is always crashing. My solution is to create a content page which is identical as your splash screen and set it at the beginning of App.xaml.cs constructor.

  • batmacibatmaci DEMember ✭✭✭✭✭
    If you are checking bearertoken from facebook or google login, you can Just use firebase to register them and verify for validity every time, it allows you to check it already on the native level, so It works before setting mainpage.
  • JohnHardmanJohnHardman GBUniversity mod

    @PieterjanDeClippel said:
    In either case the MainPage is set before the LoadApplication command is called.

    LoadApplication calls SetPage, passing it the Application.Current.MainPage . If that is null, SetPage throws an ArgumentNullException.

    In your cut down code, you call LoadApplication(new Issue.App());
    Your Issue.App constructor calls, but does not await LoadData
    Your LoadData awaits various async methods BEFORE setting MainPage (in the case where it causes an error).
    When LoadData awaits a method, it yields control back to Issue.App().
    Yielding control allows Issue.App to complete and hence LoadApplication to be called, all BEFORE MainPage is set.
    LoadApplication being called before MainPage is set results in SetPage throwing the exception.

    There are various places where awaiting asynchronous methods is a no-no. The App() constructor before MainPage is set is one.

  • MikilllMikilll Member ✭✭✭

    @JohnHardman Thank you for your response. However, I think that this is a common scenario when you've some login flow in your application and before setting the main page you have to make a web request to validate the token which is stored in the device. How to properly solve this problem? Because I personally was always making a page called LoadingPage() which was exactly the same as the splash screen and at the beginning of App constructor I was setting:

    MainPage = new LoadingPage();

    However this has some drawbacks because you have to make this LoadingPage() exactly the same as the splash screen which is sometimes difficult.

    Thus, I am curious what are your solutions to this problem :smile:

  • @JohnHardman off course.
    Interesting remark.
    Pretty easy to fix though.
    Let me just try something.

  • JohnHardmanJohnHardman GBUniversity mod
    edited October 2018

    @Mikilll said:
    How to properly solve this problem? Because I personally was always making a page called LoadingPage() which was exactly the same as the splash screen
    ...
    Thus, I am curious what are your solutions to this problem :smile:

    It's not something I have to handle currently, so other than identifying why the exception is reported, I haven't given it any thought (I have, however, given lots of thought to other scenarios in which using await is best avoided).

    Off the top of my head...

    If the work that needs doing is synchronous, doesn't take long, and needs to be completed before the main page is displayed, simply do it on the UI thread after the splash screen has appeared.

    If the work that needs to doing takes a long time and/or is asynchronous, and needs to (or appears to need to) be completed before the "main" page of the application is displayed, either:
    (a) do what you did, and insert a dummy page that replicates the splash screen, possibly with an activity indicator (whether the XF one or another one)
    (b) be honest and transparent with the user, by displaying a new page that says what is happening (synch'ing with the server, etc) and gives an indication of how long it will be before the real main page appears
    (c) show the main page but disabling any functionality that is dependent upon the prerequisite work having completed until such time as it does (with the work happening on a new task)
    (d) an extension of (c) - fire off any requests to the server, but allow the user to navigate through the app only so far as the user can go until particular requests complete (probably similar to how you handle permissions - checking the permission just before the functionality needing it is accessed).

    There are probably others :-)

  • I've found a workaround using the WaitHandle (in my case the ManualResetEvent).
    https://github.com/PieterjanDeClippel/Xamarin.Forms-HttpClient-Issue/

  • JohnHardmanJohnHardman GBUniversity mod

    @PieterjanDeClippel said:
    I've found a workaround using the WaitHandle (in my case the ManualResetEvent).
    https://github.com/PieterjanDeClippel/Xamarin.Forms-HttpClient-Issue/

    That's another option (although not one I would favor) - effectively making the startup synchronous, blocking the UI thread until all of the network operations are complete. How long those operations could take, or what to do in the event of an error, are things to consider.

  • PieterjanDeClippelPieterjanDeClippel USMember ✭✭
    edited October 2018

    After giving it a thought, I think it's better to indeed (option a)

    do what you did, and insert a dummy page that replicates the splash screen, possibly with an activity indicator (whether the XF one or another one)

    It's indeed a little over-engineered to use a ManualResetEvent and doesn't provide a better user-experience at all.

    Thanks for the ideas and thoughts

  • NMackayNMackay GBInsider, University mod

    @JohnHardman said:

    @PieterjanDeClippel said:
    I've found a workaround using the WaitHandle (in my case the ManualResetEvent).
    https://github.com/PieterjanDeClippel/Xamarin.Forms-HttpClient-Issue/

    That's another option (although not one I would favor) - effectively making the startup synchronous, blocking the UI thread until all of the network operations are complete. How long those operations could take, or what to do in the event of an error, are things to consider.

    Yup, I'd suggest this isn't a great options exactly for the reasons John highlights, if you have an unresponsive endpoint the app could be sitting there for 30 secs by which time the user has probably killed the process.

Sign In or Register to comment.