[Proposal] Add `Popup.CloseAsync()`
See original GitHub issueFeature name
Add Popup.CloseAsync()
Link to discussion
Discussed in June 2023 Standup:
https://github.com/CommunityToolkit/Maui/wiki/2023-June-Standup
Progress tracker
- Android Implementation
- iOS Implementation
- MacCatalyst Implementation
- Windows Implementation
- Tizen Implementation
- Unit Tests
- Samples
- Documentation: https://github.com/MicrosoftDocs/CommunityToolkit/pull/285
Summary
This Proposal adds the following API to Popup
:
public async Task CloseAsync(object? result = null);
CloseAsync()
returns once the operating system has dismissed Popup
from the page.
Motivation
Currently, Popup
only offers one method to programmatically dismiss it from the screen: public void Close();
.
However, on MacCatalyst and iOS, the code required to dismiss the Popup uses async/await to dismiss the UIViewController
:
The existing Close()
API is thus acting in a fire-and-forget manner; the method is returning to the caller before the Popup has been dismissed on iOS + MacCatalyst.
Detailed Design
IPopup.shared.cs
This API update requires a TaskCompletionSource
in IPopup
that can be referenced by both the Handler and the Control.
This is required because the PropertyMappers / CommandMappers that .NET MAUI use for Handlers are not asynchronous (they cannot be await
d).
We will instead tell the Control to await PopupDismissedTaskCompletionSource.Task
after IPopup.Closed()
has been called. Then, in PopupHandler.MapOnClosed
, we will call PopupDismissedTaskCompletionSource TrySetResult()
after the operating system has dismissed the Popup
from the page.
public interface IPopup : IElement, IVisualTreeElement
{
// ...
// Existing APIs
//..
/// <summary>
/// <see cref="TaskCompletionSource"/> that completes when the operating system has dismissed <see cref="IPopup"/> from the screen
/// </summary>
TaskCompletionSource PopupDismissedTaskCompletionSource { get; }
}
Popup.shared.cs
This Proposal also requires a new API public Task Popup.CloseAsync(object? result = null)
;
/// <summary>
/// Close the current popup.
/// </summary>
/// <remarks>
/// Returns once the operating system has dismissed the <see cref="IPopup"/> from the page
/// </remarks>
/// <param name="result">
/// The result to return.
/// </param>
public async Task CloseAsync(object? result = null)
{
await OnClosed(result, false);
taskCompletionSource.TrySetResult(result);
}
Usage Syntax
async void Button_Clicked(object? sender, EventArgs e)
{
await CloseAsync();
await Toast.Make("Popup Dismissed By Button").Show();
}
Drawbacks
Only iOS + MacCatalyst defer to a different thread when dismissing the Popup; Android and Windows can dismiss the Popup synchronously. Adding a method that returns Task
adds a bit of overhead to our users, however, the overhead should be negligible as users typically only display, and subsequently close, one Popup at a time.
Alternatives
This Proposal can be updated to remove the fire-and-forget method, void Close()
.
However, removing an existing API is a breaking change.
Unresolved Questions
Should we instead return ValueTask
?
public async ValueTask CloseAsync(object? result = null)
Issue Analytics
- State:
- Created 4 months ago
- Comments:8 (3 by maintainers)
Yup! Thanks, I’ve added linked it to the PR.
I’m happy to approve this also