Eduardo Coelho
Eduardo Coelho

Reputation: 1963

C# / MonoTouch: how to call an async method when a ViewController is created/appears

Xamarin released support for async/await which really simplifies the development of responsive UI in mobile platforms. I'd like to take advantage of it and increase the programming level of my code by using async/await stuff from now on.

However, since I'm relatively new to C# and haven't used async/await before I'm having trouble to find 'hooks' in my code that I can invoke async methods. I know that event handlers are the typical places (where IoC happens), but imagine the following scenario:

I want to start background task when a ViewController is loaded (as opposed to when a button is pressed).

async Task PerformMyTaskAsync ()
{
    // ...
    await ... // another async API
    // ...
}

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // .. initialize UI
    await PerformMyTaskAsync ();
}

Obviously I can't await for PerformMyTaskAsync in ViewDidLoad because ViewDidLoad is neither an async method nor an event handler.

What is the 'alternative' approach to start a background task when a view controller loads (or appears, whatever) ?

Upvotes: 9

Views: 4710

Answers (3)

Chris Moschini
Chris Moschini

Reputation: 37967

So, running something async off the UI thread, from an event, looks like this:

Blah.OnEvent += onEvent;

protected async void onEvent(object sender, EventArgs ev)
{
...
}

The void is not a typo - it's the antipattern that Xamarin follows for async UI events, unfortunately.

There are valid scenarios where you would want to run an async method that makes the UI wait. For example, calling Navigation.PushAsync() off of a button click. But, you specifically asked for a background task, which implies it could take a while. For that, you don't want to make the UI wait; you should start the task and return immediately. For that you just use the same background task code you'd use in any C# TPL app:

    public static void RunBg(Func<Task> fn)
    {
        Task.Run(fn).ConfigureAwait(false);
            // Uncomment to roll up exceptions
            //.GetAwaiter().GetResult();
    }

    public static void RunBgLong(Func<Task> fn)
    {
        Task.Factory.StartNew(fn, TaskCreationOptions.LongRunning).ConfigureAwait(false);
            // Uncomment to roll up exceptions
            //.GetAwaiter().GetResult();
    }

I provide both because the creators of the TPL have gone out of their way to provide both underneath the hood. The meaning of the 2 is better explored in other questions, though I believe the distinction for "long" is a small number of milliseconds.

So, you'd start your task like:

public override void ViewDidLoad()
{
    base.ViewDidLoad();
    TaskHelper.RunBg(async () => {
        await someBgAsyncMethodGoesHere(arg1, arg2);
    );
}

Upvotes: 0

kwcto
kwcto

Reputation: 3504

The newest Xamarin Stable Channel releases support Async/Await overloads for ViewController Lifecycle methods. Try:

public async override void ViewDidLoad()

Upvotes: 19

Jason
Jason

Reputation: 89117

Not sure if this is the "best" approach, but have you tried firing an event from within ViewDidLoad(), and then in the handler for that event launching your Async code?

Upvotes: 0

Related Questions