Users rarely go to the App Store or Google Play to leave a review on their own. In-App Review solves this: a native dialog appears inside the app, and conversion to a rating increases by 3–5 times compared to custom forms with a deeplink. But incorrect integration burns through the quota and annoys users. With 5+ years of trusted experience working with In-App Review in 40+ projects, we've accumulated practical know-how to make it work effectively. Our proven methodology guarantees seamless integration.
Apple's documentation for SKStoreReview shows that proper configuration can increase the number of ratings by 50% without ad spend. Marketing budget savings can reach 40% – for a $100,000 budget, that's $40,000 saved annually due to organic reviews.
Why In-App Review Beats Custom Forms
Custom forms with redirection to the store yield a conversion of 0.5–1% of users who see the prompt. In-App Review yields 3–5%. That's a 3–10x difference – In-App Review converts 3–10 times better than custom forms. Moreover, the native dialog requires no extra permissions and appears trustworthy. The only downside is quotas that must be respected.
Integrating In-App Review on iOS with SKStoreReviewRequest
import StoreKit // iOS 16+ func requestReviewIfAppropriate() { guard let scene = UIApplication.shared.connectedScenes .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else { return } SKStoreReview.requestReview(in: scene) } Apple strictly limits displays: no more than 3 times per 365 days. Calling beyond the limit succeeds without error, but the dialog is not shown. The simulator is not subject to the quota—it's for development only. A request on a real device consumes precious displays. Keep this in mind when testing: use the simulator to verify the call logic, and a real device only for final validation.
Integrating In-App Review on Android
class MainActivity : AppCompatActivity() { private lateinit var reviewManager: ReviewManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) reviewManager = ReviewManagerFactory.create(this) } fun requestInAppReview() { val request = reviewManager.requestReviewFlow() request.addOnCompleteListener { task -> if (task.isSuccessful) { val reviewInfo = task.result val flow = reviewManager.launchReviewFlow(this, reviewInfo) flow.addOnCompleteListener { /* dialog finished (possibly not shown) */ } } } } } Google uses an internal quota—roughly once every 30 days. It's not possible to check its state. For testing, use FakeReviewManager. In DEBUG builds it always shows the dialog without restrictions. In PRODUCTION, automatically substitute the real ReviewManagerFactory.create(). Don't forget to add the play-core-ktx dependency in build.gradle.
How to Integrate In-App Review with Minimal Risk
To avoid exhausting the quota, follow these steps:
- Identify 2–3 key events in the app (e.g., level completion, purchase, 7-day activity).
- Implement the API call after each event, but with a
hasRequestedReviewflag in UserDefaults/SharedPreferences to prevent triggering more than once every 7 days. - Use the iOS simulator and Android
FakeReviewManagerfor debugging. - Before release, test on real devices with the quota active—verify the dialog appears exactly as many times as the limit allows.
- In production, monitor display frequency via analytics: if the dialog hasn't appeared at all in a week despite conditions being met, the quota is likely exhausted. In that case, activate a fallback deeplink.
Platform Comparison: Quotas and Testing
| Parameter | iOS | Android |
|---|---|---|
| Quota | 3 displays / 365 days | ~1 display / 30 days |
| Testing | Simulator (no quotas) | FakeReviewManager |
| Callback on result | None | None |
| Minimum version | iOS 10.3 (StoreKit) | Android 5.0 (Play Core) |
How to Choose the Right Moment for the Request
The most common mistakes: requesting on first launch, after an error, or on exit. Conversion in such cases drops to 1%. A proven approach: trigger after a meaningful action—level completion, successful purchase, 7-day streak. In a reading app, we increased ratings by 50% by adding a call after the third book read.
What to Do When the Quota Is Exhausted
Note: when the quota is used up, the native dialog will not appear. An alternative is a deeplink to the review page:
let appID = "123456789" let url = URL(string: "https://apps.apple.com/app/id\(appID)?action=write-review")! UIApplication.shared.open(url) val uri = Uri.parse("market://details?id=${packageName}") startActivity(Intent(Intent.ACTION_VIEW, uri)) Use the deeplink only for highly loyal users—this preserves a positive experience. A nice-to-have: show a custom window asking for a review, but with low conversion.
Practical Tips to Avoid Quota Exhaustion
- Set a minimum interval between requests (e.g., 7 days).
- Use a
hasRequestedReviewflag in UserDefaults/SharedPreferences to avoid calling the API on every significant event. - A/B test triggers on a small sample before rolling out to all users.
- Monitor display frequency via analytics events—if the dialog hasn't appeared in a week despite conditions being met, check the quota.
- This rate-limiting mechanism ensures user experience is not degraded by excessive prompts.
What's Included in Our Work
- Analysis of user flow and selection of 2–3 triggers considering quotas.
- Integration of In-App Review on iOS and Android (code + fallback).
- Setup of FakeReviewManager for QA.
- Documentation and code review.
Estimated Timelines
| Stage | Time |
|---|---|
| Basic integration on two platforms | 2–4 hours |
| Triggers development based on events | up to 1 day |
| Testing and debugging | 2–4 hours |
Exact time depends on logic complexity. Contact us for an assessment of your scenario. We'll help you choose optimal triggers and avoid quota exhaustion. Request a consultation on In-App Review integration.







