A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
Hello @Kim Strasser ,
Thanks for your question.
I recommend using the community-created Plugin.Maui.AppRating, which has gained over 57,000 downloads. It wraps Apple's native SKStoreReviewController under the hood, so the popup your players see is the exact same standardized iOS prompt Apple provides. For more information, you can refer to this usage and code example.
- Install the NuGet package
dotnet add package Plugin.Maui.AppRating
- Register in
MauiProgram.cs
using Plugin.Maui.AppRating;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddTransient<MainPage>();
builder.Services.AddSingleton<IAppRating>(AppRating.Default);
return builder.Build();
}
}
- Code example in your game page
using Plugin.Maui.AppRating;
public partial class GamePage : ContentPage
{
private readonly IAppRating _appRating;
private const string IOSApplicationId = "idYOURAPPID";
private const string AndroidPackageName = "com.yourcompany.yourgame";
public GamePage(IAppRating appRating)
{
InitializeComponent();
_appRating = appRating;
_appRating.ThrowErrors = false;
}
public async Task OnLevelCompleted()
{
int levelsCompleted = Preferences.Get("levels_completed", 0) + 1;
Preferences.Set("levels_completed", levelsCompleted);
if (levelsCompleted % 5 == 0)
{
await CheckAndShowReviewPrompt();
}
}
private async Task CheckAndShowReviewPrompt()
{
if (Preferences.Get("app_rated", false))
return;
var lastShownStr = Preferences.Get("last_review_shown", string.Empty);
if (!string.IsNullOrEmpty(lastShownStr))
{
var lastShown = DateTime.Parse(lastShownStr);
var daysPassed = (DateTime.UtcNow - lastShown).TotalDays;
if (daysPassed < 30)
return;
}
await MainThread.InvokeOnMainThreadAsync(async () =>
{
bool wantsToRate = await DisplayAlert(
"Enjoying the game?",
"Would you like to leave a quick review? It really helps us!",
"Sure!", "Maybe later"
);
if (!wantsToRate)
{
Preferences.Set("levels_completed", 0);
return;
}
await ShowNativeReviewPopup();
});
}
private async Task ShowNativeReviewPopup()
{
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await _appRating.PerformInAppRateAsync();
});
Preferences.Set("last_review_shown", DateTime.UtcNow.ToString());
Preferences.Set("app_rated", true);
}
private async void OnRateUsButtonClicked(object sender, EventArgs e)
{
if (Preferences.Get("app_rated", false))
return;
await MainThread.InvokeOnMainThreadAsync(async () =>
{
try
{
await _appRating.PerformRatingOnStoreAsync(
applicationId: IOSApplicationId,
packageName: AndroidPackageName
);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Rating error: {ex.Message}");
}
});
Preferences.Set("app_rated", true);
}
}
Is it necessary to check if the prompt was already displayed three times in a 365-day period?
No, you do not need to check or count this yourself. Apple handles it automatically.
Unlike Android where Google does not publish their exact quota, Apple is transparent and officially documents the limit. Here is what Apple officially says:
"Be aware that the system displays the review prompt to a user a maximum of three times within a 365-day period"
However, it is still good practice - and highly recommended by Apple to add your own minimum interval so you are not calling the API after every single level. The 30-day check in the code above does exactly that.
The other points follow the same idea as discussed in the Android thread.
While these links are non-Microsoft links, they are official Github, Nuget and Android documentations and are safe to visit.
I hope this addresses your question. If this response was helpful, please consider following the guidance to provide feedback.