SKStoreReviewController in 2026: how to ask for App Store ratings without annoying people (Swift)
SKStoreReviewController is deprecated. What replaced it, the three-prompts-a-year rule, working SwiftUI and UIKit code, and when to ask so the rating is a good one.
Ratings are half of what Apple says moves search rank. The $99 audit checks your rating prompt timing alongside your keywords, listing and funnel.
Book the $99 auditThe 30-second answer
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.
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 as of October 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:
| 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:) and the ratings and reviews HIG. 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 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.
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 ?? ""
}
}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:
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.
// 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 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, 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:
| 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 (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 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 to appear.
- Reset the rating only for a real fix. When you release a new version you can reset the 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 says ratings and reviews "can influence how your app ranks in App Store search" — see our App Store ranking factors for how they sit next to your name, subtitle and keywords.
Ship checklist
- Replace any
SKStoreReviewControllercalls with@Environment(\.requestReview)orAppStore.requestReview(in:). - Call it from a success moment, gated on a few completed actions, a few days since install, and once per version.
- Remove any call tied to a button; point buttons at the
action=write-reviewURL. - Take the prompt out of launch and onboarding.
- Check the flow in a development build, where the prompt always appears — TestFlight testers will never see it.
- 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.
Keep reading
For AI agents · readable sources
More ratings start with the right moment.
The $99 audit reads your listing, keywords and funnel — including when and where your app asks for a rating — and gives you a 30-day plan, credited against month one if you keep working with us.