Could creating an event be a valid way to make a XF OnAppearing into an async method?

Viewed 197

I recently saw this suggestion on how to make an app OnStart into an async OnStart:

    protected override void OnStart()
    {
        this.started += onStarted;      //Subscribe to event
        started(this, EventArgs.Empty); //Raise event
    }

    protected async void onStarted(object sender, EventArgs args)
    {
        try
        {
            await // do things
        }
        catch (Exception ex)
        {
            var ignore = ex;
        }
        this.started -= onStarted;
    }

Can anyone see any possible issues with this and if not then could something similar be applied to the OnAppearing and if so would there be any changes needed.

1 Answers

OnAppearing is simply a void method on Page class

Xamarin.Forms.Page

//...

protected virtual void OnAppearing()
{
}

//...

Source

that is called as part of the Page's life cycle.

//...

[EditorBrowsable(EditorBrowsableState.Never)]
public void SendAppearing()
{
    if (_hasAppeared)
        return;

    _hasAppeared = true;

    if (IsBusy)
    {
        if (IsPlatformEnabled)
            MessagingCenter.Send(this, BusySetSignalName, true);
        else
            _pendingActions.Add(() => MessagingCenter.Send(this, BusySetSignalName, true));
    }

    OnAppearing(); //<---
    Appearing?.Invoke(this, EventArgs.Empty); //NOTE HOW ACTUAL EVENT IS RAISED AFTER

    var pageContainer = this as IPageContainer<Page>;
    pageContainer?.CurrentPage?.SendAppearing();

    FindApplication(this)?.OnPageAppearing(this);
}

//...

Source

They are not to be mistaken for event handlers, that are the one exception allowed to use async void.

Reference Async/Await - Best Practices in Asynchronous Programming

The approach shown in your OnStart example can also be applied to OnAppearing

For example

public partial class SomePage : ContentPage {
    public SomelPage() {
        InitializeComponent();
        appearing += onAppearing;
    }

    protected override void OnAppearing() {
        appearing(this, EventArgs.Empty);
        appearing -= onAppearing;
    }

    event EventHandler appearing = delegate { };

    private async void onAppearing(object sender, EventArgs args) {
        try {
            var locator = CrossGeolocator.Current;
            var position = await locator.GetPositionAsync();

            var places = await SomeService.getPlacesOfInterest(position.Latitude, position.Longitude);
            placesListView.ItemsSource = places;
        } catch( Exception ex) {
            //handler error (Log?)
        }
    }
}

Or you could subscribe to the actual Appearing event directly

//...

public event EventHandler Appearing;

//...

Source

and forgo overriding the OnAppearing() method

public partial class SomePage : ContentPage {
    public SomelPage() {
        InitializeComponent();
        Appearing += onAppearing;
    }

    private async void onAppearing(object sender, EventArgs args) {
        try {
            var locator = CrossGeolocator.Current;
            var position = await locator.GetPositionAsync();

            var places = await SomeService.getPlacesOfInterest(position.Latitude, position.Longitude);
            placesListView.ItemsSource = places;
        } catch( Exception ex) {
            //handler error (Log?)
        }
    }
}
Related