Search code examples
c#androidxamarinxamarin.formsxamarin-studio

Nested Async and Await in Forms


I'm using Xamarin Forms with Xamarin Studio on a Mac Mini, and I'm having an async/await issue I was hoping someone could help with. I have a button click event defined as:

void LoginButton_Clicked (object sender, EventArgs e)
{
  //Do some stuff
  this.HandleSignInCompleted(..);
}

This then calls a method to do some user profile initialization, which it needs to contact the server:

async void HandleSignInCompleted(..)
{
  var profile = await _profileService.GetAsync ().ConfigureAwait(false);

This waits indefinitely, whether ConfigureAwait is present or not. The user profile service simple does the following right now (just returns myself for testing):

    public async Task<UserProfile> GetAsync()
    {
        //wrapper around app properties
        var accessToken = (string)_appProperties.Get ("token"); 

        //Tried with ConfigureAwait and without
        await Task.Delay (100);

        //TODO:replace with facebook load
        return new UserProfile (..);
    }

With ConfigureAwait(false), Task.Delay chokes. Without it, it works, but the debugger crashed. What's going on and how can I resolve this issue so that the AJAX request (when I replace it in the user profile service in the future) will work?


Solution

  • Sync-over-async is an antipattern; you're at the mercy not only of your own code (must use ConfigureAwait(false) everywhere), but also at the mercy of any libraries you call. In particular, I've run into a similar issue on Xamarin in the past regarding Task.Delay. And before you try switching it out for a real network call, HttpClient has the same problem on some mobile platforms (either iOS or Android, don't remember ATM). Not sure if Facebook would have a similar bug or not.

    So, in summary, you really just can't do that. Instead, embrace async all the way:

    async Task HandleSignInCompletedAsync(..)
    {
      var profile = await _profileService.GetAsync ().ConfigureAwait(false);
    }
    
    async void LoginButton_Clicked (object sender, EventArgs e)
    {
      //Do some stuff
      await this.HandleSignInCompletedAsync(..);
    }
    

    Disable controls if you have to, but don't block. A nice side effect of this approach is that your app is always more responsive.