---
title: "SKStoreReviewController in 2026: how to ask for App Store ratings without annoying people (Swift)"
canonical: "https://asoitis.com/blog/skstorereviewcontroller"
description: "SKStoreReviewController is deprecated since iOS 18. Use requestReview in SwiftUI or AppStore.requestReview(in:) — with Swift code, Apple's limits and timing."
kind: "blog-post"
category: "ASO"
datePublished: "2026-10-09"
dateModified: "2026-10-09"
keywords:
  - "skstorereviewcontroller"
  - "skstorereviewcontroller deprecated"
  - "requestreview swiftui"
  - "appstore.requestreview"
  - "how to get more app store ratings"
  - "ios app review prompt"
  - "app store rating prompt"
---

# SKStoreReviewController in 2026: how to ask for App Store ratings without annoying people (Swift)

> SKStoreReviewController.requestReview(in:) was deprecated in iOS 18. On iOS 16 and later, use the SwiftUI requestReview environment action or AppStore.requestReview(in:) in UIKit. StoreKit shows the prompt at most three times in 365 days, decides by itself whether to show it, never shows it in TestFlight, and shouldn't be triggered by a button. Ask after a user completes something meaningful, not at launch or during onboarding.

*Published 2026-10-09 · last updated 2026-10-09 · by Ahmed Gagan for ASOitis.*

Ratings are one of the few ranking signals Apple names, and the system prompt is the only way Apple allows you to ask for them inside your app. You get three chances a year per user, no callback telling you whether the prompt appeared, and an API that changed names twice. Here's the current API, code you can paste, and the timing rules that decide whether those three chances turn into five-star ratings or wasted ones.

## SKStoreReviewController is deprecated: what to use instead

Apple has renamed the review request twice. The class still compiles, but both of its methods are now deprecated. Apple's documentation for `requestReview(in:)` says simply: "Use AppStore.requestReview(in:)." Here is where each API stands, from Apple's [StoreKit documentation](https://developer.apple.com/documentation/storekit/skstorereviewcontroller) as of October 9, 2026:

**Apple's review request APIs, by status (checked Oct 9, 2026)**

| API | Introduced | Status | Use it for |
| --- | --- | --- | --- |
| `SKStoreReviewController.requestReview()` | iOS 10.3 | Deprecated in iOS 14 | Nothing new |
| `SKStoreReviewController.requestReview(in:)` | iOS 14 | Deprecated in iOS 18 | A fallback if you still support iOS 14–15 |
| `AppStore.requestReview(in:)` | iOS 16 | Current | UIKit apps, with a `UIWindowScene` |
| `RequestReviewAction` (`@Environment(\.requestReview)`) | iOS 16 | Current | SwiftUI apps |

No newer review API shipped with iOS 26: StoreKit's Reviews section still lists only `RequestReviewAction`, `SKStoreReviewController` and Apple's sample project. If your deployment target is iOS 16 or later — most indie apps by now — you never need to touch `SKStoreReviewController` again.

## The rules StoreKit enforces

Calling the API is a request, not a command. Apple's documentation says that "App Store policy governs the actual display of a rating and review request view". The behaviour is the same for all three current methods:

**How StoreKit handles a review request, in Apple's words**

| Situation | What happens |
| --- | --- |
| The person hasn't rated your app on this device | Shown "a maximum of three times within a 365-day period" |
| The person has already rated or reviewed it | Shown only "if the app version is new, and if more than 365 days have passed since the person's previous review" |
| Development builds run from Xcode | "StoreKit always displays the rating and review request view" |
| TestFlight builds | The method "has no effect" |
| The person turned prompts off | Nothing. Apple's HIG notes people "can also opt out of receiving these prompts for all apps" |

Those rules come from Apple's documentation for [AppStore.requestReview(in:)](https://developer.apple.com/documentation/storekit/appstore/requestreview%28in%3A%29-1q8qs) and the [ratings and reviews HIG](https://developer.apple.com/design/human-interface-guidelines/ratings-and-reviews). Two consequences matter more than the limit itself. The call returns nothing, so your app can't tell whether the prompt actually appeared. And because it may not appear, Apple says: "don't call requestReview() or requestReview(in:) in response to a button tap or other user action." A "Rate us" button that silently does nothing is a bug report waiting to happen. For a button, use the write-review link further down.

## SwiftUI: requestReview, gated on real use

Apple's own [sample project](https://developer.apple.com/documentation/storekit/requesting-app-store-reviews) waits until someone has completed its main task four times, never asks twice for the same app version, and pauses a couple of seconds before asking. Apple says those conditions "exist purely to delay the call". Do the same: keep a small policy object that records successes and decides when a request has been earned.

**ReviewPolicy.swift — decides when a request is earned**

```swift
import Foundation

/// Records meaningful successes and decides when a review request is earned.
enum ReviewPolicy {
    private static var defaults: UserDefaults { .standard }
    private static let successKey = "review.successCount"
    private static let versionKey = "review.lastPromptedVersion"
    private static let installKey = "review.firstLaunch"

    /// Call once at launch.
    static func registerLaunch() {
        if defaults.object(forKey: installKey) == nil {
            defaults.set(Date.now, forKey: installKey)
        }
    }

    /// Call when the user finishes the thing your app is for.
    static func recordSuccess() {
        defaults.set(defaults.integer(forKey: successKey) + 1, forKey: successKey)
    }

    static var shouldRequest: Bool {
        let installed = defaults.object(forKey: installKey) as? Date ?? .now
        let days = Calendar.current.dateComponents([.day], from: installed, to: .now).day ?? 0
        return defaults.integer(forKey: successKey) >= 3
            && days >= 3
            && defaults.string(forKey: versionKey) != currentVersion
    }

    static func markRequested() {
        defaults.set(currentVersion, forKey: versionKey)
    }

    private static var currentVersion: String {
        Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
    }
}
```

**WorkoutCompleteView.swift — ask on the success screen**

```swift
import StoreKit
import SwiftUI

struct WorkoutCompleteView: View {
    @Environment(\.requestReview) private var requestReview

    var body: some View {
        CompletionSummary() // your success screen
            .task {
                guard ReviewPolicy.shouldRequest else { return }
                // Let the moment land before the system sheet appears.
                do { try await Task.sleep(for: .seconds(2)) } catch { return }
                requestReview()
                ReviewPolicy.markRequested()
            }
    }
}
```

Call `ReviewPolicy.recordSuccess()` where the work is actually saved — the workout logged, the export finished — not in a view's `onAppear`, which can fire more than once. The thresholds (three successes, three days, once per version) are a starting point; Apple's HIG suggests "at least a week or two between requests".

## UIKit: AppStore.requestReview(in:)

UIKit apps pass the window scene. If your deployment target is still below iOS 16, keep the deprecated call as a fallback behind an availability check:

**Requesting a review from a view controller**

```swift
import StoreKit
import UIKit

@MainActor
func requestReviewIfEarned(from viewController: UIViewController) {
    guard ReviewPolicy.shouldRequest,
          let scene = viewController.view.window?.windowScene else { return }

    if #available(iOS 16.0, *) {
        AppStore.requestReview(in: scene)
    } else {
        SKStoreReviewController.requestReview(in: scene)
    }
    ReviewPolicy.markRequested()
}
```

## The "Rate this app" button: use the write-review link

A permanent "Rate us" or "Write a review" row in Settings is fine — it just shouldn't call the prompt API. Apple's documentation says to append `action=write-review` to your product page URL "to automatically open the App Store page where users can write a review." This always works, doesn't use up one of the three prompts, and suits people who want to leave a written review rather than just stars.

**A Settings row that opens the review page**

```swift
// Replace 1234567890 with your app's numeric Apple ID (App Store Connect > App Information).
private let writeReviewURL = URL(string: "https://apps.apple.com/app/id1234567890?action=write-review")!

Link("Write a review", destination: writeReviewURL)
```

## When to ask: after a success, not during onboarding

Apple is unusually direct about timing. Its [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/ratings-and-reviews) say:

> Avoid asking for a rating on first launch or during onboarding, because people haven't had enough time to gain a clear understanding of your app's value or form an opinion. People may even be more likely to leave negative feedback if they feel an app is asking for a rating before they get a chance to use it.

Some of the biggest subscription apps do it anyway. In our [QUITTR teardown](https://asoitis.com/case-studies/quittr), recorded October 7, 2026, Apple's system prompt — "Enjoying QUITTR? Tap a star to rate it on the App Store." — appeared at step 12 of onboarding, over a custom "Give us a rating" screen with user reviews, after the tracking prompt and before the paywall. Our tester hadn't used the app at all. The listing showed 33,689 ratings at 4.72 stars that day.

So it can work — for an app whose onboarding is itself an emotional commitment, at enormous install volume. For most indie apps it spends one of the year's three prompts on a person who has no opinion yet, which is exactly the risk Apple's guidance describes. The better default is the moment your app delivers on its promise:

**Good moments to request a review, by app type**

| App type | Ask after | Avoid |
| --- | --- | --- |
| Fitness, habits | A completed workout or a streak milestone | Launch, a missed day |
| Productivity, utilities | A finished export, scan or project | Mid-task, an error screen |
| Calorie and health tracking | A logged week, a goal hit | The first log |
| Games | A completed level | During play, after a loss |
| Subscriptions | A successful renewal period of active use | The paywall, a cancellation flow |

> According to ASOitis, because StoreKit shows the review prompt at most three times a year and tells your app nothing about whether it appeared, timing is the only part of the rating request a developer controls — so every request should follow a completed success, never a launch, a button tap or a step of onboarding.

## What App Review won't allow

The [App Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) (last updated June 8, 2026) close the obvious shortcuts:

- **Custom rating prompts.** Guideline 5.6.1: "Use the provided API to prompt users to review your app… we will disallow custom review prompts." Your own star-rating dialog that sends people to the App Store is out.
- **Forcing a rating.** Guideline 3.2.2(x): apps "must not force users to rate the app, review the app… in order to access functionality, content, or use of the app." No locked features until someone rates.
- **Paid, incentivized or filtered reviews.** The Business section warns that manipulating reviews with "paid, incentivized, filtered, or fake feedback" can get you expelled from the Apple Developer Program. A coin reward for a rating is incentivized; asking "Do you like the app?" and only sending happy users to the prompt is filtering.

If you've been rejected for something like this, our [App Review library](https://asoitis.com/app-review) covers the guidelines that reject indie apps most often, with the reply to send.

## After the rating: responses and resets

- **Reply to reviews.** Apple says that when you respond, "the reviewer is notified and has the option to update their review". Start with the lowest ratings and bug reports. Replies [can take up to 24 hours](https://developer.apple.com/help/app-store-connect/monitor-ratings-and-reviews/respond-to-reviews) to appear.
- **Reset the rating only for a real fix.** When you release a new version you can [reset the overview rating](https://developer.apple.com/help/app-store-connect/monitor-ratings-and-reviews/reset-an-app-overview-rating) in all regions at once; written reviews stay, and the old rating can't be restored. Apple warns that "having few ratings may discourage potential users from downloading your app."
- **Remember why it matters.** Apple's [search page](https://developer.apple.com/app-store/search/) says ratings and reviews "can influence how your app ranks in App Store search" — see our [App Store ranking factors](https://asoitis.com/blog/app-store-ranking-factors) for how they sit next to your name, subtitle and keywords.

## Ship checklist

1. Replace any `SKStoreReviewController` calls with `@Environment(\.requestReview)` or `AppStore.requestReview(in:)`.
2. Call it from a success moment, gated on a few completed actions, a few days since install, and once per version.
3. Remove any call tied to a button; point buttons at the `action=write-review` URL.
4. Take the prompt out of launch and onboarding.
5. Check the flow in a development build, where the prompt always appears — TestFlight testers will never see it.
6. Reply to low ratings within a day or two of each release.

## Frequently asked questions

### Is SKStoreReviewController deprecated?

Yes. SKStoreReviewController.requestReview() was deprecated in iOS 14 and requestReview(in:) in iOS 18. Apple's documentation points to AppStore.requestReview(in:) for UIKit; SwiftUI apps use the requestReview environment action. Both need iOS 16 or later.

### How many times can I show the App Store rating prompt?

StoreKit shows it at most three times within a 365-day period for someone who hasn't rated your app on that device. If they already rated it, it can appear again only for a new app version and more than 365 days after their previous review. The system decides; your call is only a request.

### Why doesn't the review prompt appear in TestFlight?

Apple's documentation says the request has no effect in apps distributed for beta testing through TestFlight. In development builds run from Xcode it always appears, so test the flow there.

### Can I add a "Rate us" button that shows the prompt?

Apple says not to call the review request in response to a button tap, because it may not present anything. Use a link to your App Store URL with action=write-review appended instead; it always opens the review page.

### Can I make my own rating popup?

No. Guideline 5.6.1 says to use the provided API and that Apple will disallow custom review prompts. Forcing a rating to unlock features is banned by Guideline 3.2.2(x), and incentivized or filtered reviews can get you removed from the Developer Program.

### When is the best time to ask for a rating?

After the user has completed something meaningful in your app — a finished workout, project or level — and not at launch or during onboarding, which Apple's Human Interface Guidelines advise against. Leave at least a week or two between requests.

## See also

- [App Store ranking factors: where ratings fit](https://asoitis.com/blog/app-store-ranking-factors)
- [QUITTR teardown: a rating prompt inside onboarding](https://asoitis.com/case-studies/quittr)
- [App Store rejection fixes, guideline by guideline](https://asoitis.com/app-review)
- [What an ASO audit checks](https://asoitis.com/blog/aso-audit-for-ios-apps)


## About ASOitis

**ASOitis** — ASO + GEO agency for indie iOS apps. Founder: Ahmed Gagan. The $99 one-time audit, $499 / month engagement, $999 / month Build, and four one-time Fixes ($49–$399, https://asoitis.com/fixes) are the entire price list. All sales final (https://asoitis.com/terms). ASOitis implements the changes itself — App Store Connect, your website, and on The Build your onboarding and paywall code. iOS-only. Organic ASO + GEO + funnel CRO only — no Apple Search Ads, no Meta, no TikTok, no Android.

- Homepage: https://asoitis.com
- About / founder: https://asoitis.com/about
- Case studies: https://asoitis.com/case-studies
- Guides: https://asoitis.com/guides
- Blog: https://asoitis.com/blog
- Full LLM documentation: https://asoitis.com/llms-full.txt
- AI training and citation permissions: https://asoitis.com/ai.txt
- Book the $99 audit: https://asoitis.com/checkout/start?plan=audit
- 15-min founder call: https://cal.com/ahmedgagan/asoitis-chat
