![]() |
VOOZH | about |
dotnet add package ReviewService.Abstractions --version 4.0.0
NuGet\Install-Package ReviewService.Abstractions -Version 4.0.0
<PackageReference Include="ReviewService.Abstractions" Version="4.0.0" />
<PackageVersion Include="ReviewService.Abstractions" Version="4.0.0" />Directory.Packages.props
<PackageReference Include="ReviewService.Abstractions" />Project file
paket add ReviewService.Abstractions --version 4.0.0
#r "nuget: ReviewService.Abstractions, 4.0.0"
#:package ReviewService.Abstractions@4.0.0
#addin nuget:?package=ReviewService.Abstractions&version=4.0.0Install as a Cake Addin
#tool nuget:?package=ReviewService.Abstractions&version=4.0.0Install as a Cake Tool
This repository introduces abstractions around native review capabilities to ease code sharing and testability. It also introduces business logic to quickly configure conditions and state tracking to prompt for reviews at the right moment.
Before getting started, please read the Android and iOS application review documentation.
Add the ReviewService and ReviewSerivce.NativePrompters NuGet packages to your projects (Windows, Android and iOS).
💡 If you need to implement more platforms or create custom implementations, you can use the
ReviewService.AbstractionsNuGet package.
Create an instance of ReviewService. We'll cover dependency injection in details later on in this documentation.
using ReviewService;
var reviewConditionsBuilder = ReviewConditionsBuilder.Empty()
.MinimumPrimaryActionsCompleted(1);
var reviewService = new ReviewService<ReviewSettings>(
logger: null,
reviewPrompter: new ReviewPrompter(logger: null),
reviewSettingsSource: new MemoryReviewSettingsSource(),
reviewConditionsBuilder: reviewConditionsBuilder
)
Use the service.
using ReviewService;
private readonly IReviewService<ReviewSettings> _reviewService;
public async Task DoPrimaryAction(CancellationToken ct)
{
// Do Primary Action.
// Track this action.
await _reviewService.TrackPrimaryActionCompleted(ct)
}
using ReviewService;
private readonly IReviewService<ReviewSettings> _reviewService;
public async Task OnCompletedImportantFlow(CancellationToken ct)
{
// Do Meaningful Task.
// Check if all conditions are satisfied and prompt for review if they are.
var result = await _reviewService.TryRequestReview(ct);
if (result.IsSuccessful)
{
// Review prompt was successfully shown
}
else
{
Console.WriteLine( $ "Review request status: {result.Status}.");
}
}
<img src="docs/review_prompt_android.gif" alt="Review Prompt Android" width="250"> <img src="docs/review_prompt_ios.gif" alt="Review Prompt iOS" width="250">
MemoryReviewSettingsSource is great for automated testing but should not be the implementation of choice for real use-cases. Instead, you should create your own implementation that persists data on the device (so that review settings don't reset when you kill the app).
using ReviewService;
/// <summary>
/// Storage implementation of <see cref="IReviewSettingsSource{TReviewSettings}"/>.
/// </summary>
/// <typeparam name="TReviewSettings">The type of the persisted object.</typeparam>
public sealed class StorageReviewSettingsSource<TReviewSettings> : IReviewSettingsSource<TReviewSettings>
where TReviewSettings : ReviewSettings
{
/// <inheritdoc/>
public Task<TReviewSettings> Read(CancellationToken ct)
{
// TODO: Return stored review settings.
}
/// <inheritdoc/>
public Task Write(CancellationToken ct, TReviewSettings reviewSettings)
{
// TODO: Update stored review settings.
}
}
Here is a simple code that does dependency injection using Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ReviewService;
var host = new HostBuilder()
.ConfigureServices(serviceCollection => serviceCollection
.AddSingleton<IReviewPrompter, ReviewPrompter>()
.AddSingleton<IReviewSettingsSource<ReviewSettingsCustom>, ReviewSettingsSource>()
.AddTransient(s => ReviewConditionsBuilder
.Default<ReviewSettingsCustom>()
)
.AddSingleton<IReviewService<ReviewSettingsCustom>, ReviewService<ReviewSettingsCustom>>()
)
.Build();
💡 We recommend that you define your own interface that wraps
IReviewService<YouChoiceOfReviewSettings>to make the usage code leaner and ease any potential refactorings./// <summary> /// This interface wraps <see cref="IReviewService{TReviewSettings}"/> so that you don't have to repeat the generic parameter everywhere that you would use the review service. /// In other words, you should use this interface in the app instead of <see cref="IReviewService{TReviewSettings}"/> because it's leaner. /// </summary> /// <remarks> /// If you would change <see cref="ReviewSettings"/> for a custom type, using this interface allows you to minimize any refactoring effort by limiting it to this interface and the associated adapter. /// </remarks> public interface IReviewService : IReviewService<ReviewSettings> { }Here's a full example.
public static class ReviewConfiguration { public static IServiceCollection AddReviewServices(this IServiceCollection services) { return services .AddTransient(s => ReviewConditionsBuilder .Empty() .MinimumPrimaryActionsCompleted(3) ) .AddSingleton<IReviewPrompter, LoggingReviewPrompter>() .AddSingleton<IReviewSettingsSource<ReviewSettings>, MemoryReviewSettingsSource<ReviewSettings>>() .AddSingleton<IReviewService<ReviewSettings>, ReviewService<ReviewSettings>>() .AddSingleton<IReviewService, ReviewServiceAdapter>(); } private sealed class ReviewServiceAdapter : IReviewService { private readonly IReviewService<ReviewSettings> _reviewService; public ReviewServiceAdapter(IReviewService<ReviewSettings> reviewService) { _reviewService = reviewService; } public Task<bool> GetAreConditionsSatisfied(CancellationToken ct) => _reviewService.GetAreConditionsSatisfied(ct); public async Task<ReviewRequestResult> TryRequestReview(CancellationToken ct) { var status = await _reviewService.TryRequestReview(ct); return new ReviewRequestResult(status); } public Task UpdateReviewSettings(CancellationToken ct, Func<ReviewSettings, ReviewSettings> updateFunction) => _reviewService.UpdateReviewSettings(ct, updateFunction); } }
Now that everything is setup, Let's see what else we can do!
To track the provided review settings you can use the following IReviewService extensions.
💡 The review request count and the last review request are automatically tracked by the service.
If you need custom conditions for your application, you have to create another record that inherits from ReviewSettings.
using ReviewService;
/// <summary>
/// The custom review prompt settings used for prompt conditions.
/// </summary>
public record ReviewSettingsCustom : ReviewSettings
{
/// <summary>
/// Gets or sets if the application onboarding has been completed.
/// </summary>
public bool HasCompletedOnboarding { get; init; }
}
To track your custom review settings, you can create extensions for IReviewService and be sure to make them generic so they are usable with custom review settings.
using ReviewService;
/// <summary>
/// Extensions of <see cref="IReviewService{TReviewSettings}"/>.
/// </summary>
public static class ReviewServiceExtensions
{
/// <summary>
/// Tracks that the application onboarding has been completed.
/// </summary>
/// <typeparam name="TReviewSettings">The type of the object that we use for tracking.</typeparam>
/// <param name="reviewService"><see cref="IReviewService{TReviewSettings}"/>.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns><see cref="Task"/>.</returns>
public static async Task TrackOnboardingCompleted<TReviewSettings>(this IReviewService<TReviewSettings> reviewService, CancellationToken ct)
where TReviewSettings : ReviewSettingsCustom
{
await reviewService.UpdateReviewSettings(ct, reviewSettings =>
{
return reviewSettings with { HasCompletedOnboarding = reviewSettings.HasCompletedOnboarding };
});
}
}
If you want to use our default review conditions, you can use ReviewConditionsBuilder.Default() and pass it to the ReviewService constructor, or register it as a transient dependency when using dependency injection. Please note that our review conditions are also generic, so they can be used with custom review settings too.
The ReviewConditionsBuilder.Default() extension method uses the following conditions.
To create custom review conditions, you have to use ReviewConditionsBuilder.Custom and ReviewConditionsBuilder.CustomAsync and provide them with a function directly instead of a condition. Also you can create extensions for IReviewConditionsBuilder and add a new condition to the builder. To create a review condition, you can use both SynchronousReviewCondition and AsynchronousReviewCondition you need to provide them with a function.
namespace ReviewService;
/// <summary>
/// Extensions for <see cref="IReviewConditionsBuilder{TReviewSettings}"/>.
/// </summary>
public static partial class ReviewConditionsBuilderExtensions
{
/// <summary>
/// The application onboarding must be completed.
/// </summary>
/// <typeparam name="TReviewSettings">The type of the object that we use for tracking.</typeparam>
/// <param name="builder">The builder.</param>
/// <returns><see cref="IReviewConditionsBuilder{TReviewSettings}"/>.</returns>
public static IReviewConditionsBuilder<TReviewSettings> ApplicationOnboardingCompleted<TReviewSettings>(this IReviewConditionsBuilder<TReviewSettings> builder)
where TReviewSettings : ReviewSettingsCustom
{
builder.Conditions.Add(new SynchronousReviewCondition<TReviewSettings>(
(reviewSettings, currentDateTime) => reviewSettings.HasCompletedOnboarding is true)
);
return builder;
}
}
Here is a simple code that uses the builder extensions for review conditions.
var reviewConditionsBuilder = ReviewConditionsBuilder.Empty()
.MinimumPrimaryActionsCompleted(1)
.MinimumSecondaryActionsCompleted(1)
.MinimumApplicationLaunchCount(1)
.MinimumTimeElapsedSinceApplicationFirstLaunch(TimeSpan.FromDays(1))
.Custom((reviewSettings, currentDateTime) =>
{
return reviewSettings.PrimaryActionCompletedCount + reviewSettings.SecondaryActionCompletedCount >= 2;
});
It's possible to customize the review conditions used by the service by using ReviewConditionsBuilder and passing it to the ReviewService constructor or by injecting it as a transient when using dependency injection.
using ReviewService;
var reviewConditionsBuilder = ReviewConditionsBuilder.Empty<ReviewSettingsCustom>()
.ApplicationOnboardingCompleted()
.MinimumPrimaryActionsCompleted(3)
.MinimumApplicationLaunchCount(3)
.MinimumTimeElapsedSinceApplicationFirstLaunch(TimeSpan.FromDays(5))
.Custom((reviewSettings, currentDateTime) =>
{
return reviewSettings.PrimaryActionCompletedCount + reviewSettings.SecondaryActionCompletedCount >= 2;
});
This is what you need to know before testing and debugging this service. Please note that this may change and you should always refer to the Apple and Android documentation for the most up-to-date information.
Take a look at StoreReviewPlugin that we use to prompt for review.
Please consult for more information about version history and compatibility.
This project is licensed under the Apache 2.0 license - see the file for details.
Please read for details on the process for contributing to this project.
Be mindful of our .
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 3 NuGet packages that depend on ReviewService.Abstractions:
| Package | Downloads |
|---|---|
|
ReviewService
A multi-platform recipe to rate mobile apps. |
|
|
ReviewService.NativePrompters
Native implementations of IReviewPrompter interface from the ReviewService.Abstractions package. |
|
|
Sample.Access
Package Description |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.0 | 370 | 1/20/2026 |
| 3.0.0 | 202 | 1/12/2026 |
| 2.0.0 | 330 | 6/26/2025 |
| 1.0.0 | 30,986 | 7/20/2023 |
| 0.2.0-dev.45 | 242 | 7/19/2023 |
| 0.2.0-dev.43 | 219 | 7/19/2023 |
| 0.2.0-dev.39 | 193 | 7/18/2023 |
| 0.1.0-dev.37 | 208 | 7/14/2023 |