Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Increase App Ratings by using SKStoreReviewController

SKStoreReviewController allows asking your users for App Store ratings from within the app. Positive ratings can help your app stand out in the App Store and attract more users. Conversions can increase when you ask the user for a rating at the right time.

While implementing rating requests is easy, it can become more complex to ask for ratings at the right time. Engaged users are more likely to post a positive rating, while you could expect a negative rating when you ask for a rating in the middle of a user’s flow. Let’s dive in and see how we can create an excellent rating request experience.

Using SKStoreReviewController to ask for ratings

SKStoreReviewController is not configurable and only defines a single method for asking the user for a review. Using the API looks as follows:

#if os(macOS)
SKStoreReviewController.requestReview()
#else
guard let scene = UIApplication.shared.foregroundActiveScene else { return }
SKStoreReviewController.requestReview(in: scene)
#endif

You can directly use the requestReview() method on macOS. For iOS, we need to get a target scene for presenting. In our case, we grab the active foreground scene using the following extension:

extension UIApplication {
    var foregroundActiveScene: UIWindowScene? {
        connectedScenes
            .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene
    }
}

The resulting popup looks as follows:

You can use SKStoreReviewController to ask users for ratings inside your app.

You can use SKStoreReviewController to ask users for ratings inside your app.

As you can see, it only takes the user a single tap to submit a rating for your app, which can be a great way to increase the number of ratings for your app.

The importance of asking your users for ratings

You might have heard about App Store Optimization (ASO) before. App Store Ratings are a big part of optimizing your App Store presence and increasing the chances of higher installs. It’s uncertain whether good reviews will get you higher up in the search results lists, but it’s for sure your app looks better with a few 5-star ratings.

You could take a step back and look at your behavior in the App Store. If you’re unsure what app to use, you will probably look at the ratings and reviews to decide which app is likely the best for your purpose. That’s why it’s essential to ask for ratings using the SKStoreReviewController.

Stay updated with the latest in Swift & SwiftUI

The 2nd largest newsletter in the Apple development community with 18,594 developers. Don't miss out – Join today:


The right moment to ask for ratings

The SKStoreReviewController API is simple and, due to that, comes with a few caveats. For example, there’s no way for us to know whether the user actually submitted a rating or whether he tapped “Not Now.” There’s also only a limited number of times we can ask the user for a rating, as Apple wants to prevent us from spamming for ratings.

In summary, these are the essential insights to be aware of:

  • The ask for rating prompt will only be displayed to a user a maximum of three times within 365 days.
  • There is no way to know whether the rating prompt showed up
  • You won’t get any feedback on whether the user submitted a rating or whether they tapped “Not now.”

In other words: you should time your request and possibly increase the chances of the user submitting a rating.

We start this implementation by adjusting the rating request method:

func askForRatingIfNeeded() {
    guard shouldAskForRating else { return }
    askForRating()
}

func askForRating() {
    Defaults[.lastVersionPromptedForReview] = applicationVersionProvider()

    #if os(macOS)
        SKStoreReviewController.requestReview()
    #else
        guard let scene = UIApplication.shared.foregroundActiveScene else { return }
        SKStoreReviewController.requestReview(in: scene)
    #endif
}

Note that we’re storing the last application version prompted for review. We will use this later when setting up conditions. The applicationVersionProvider is just a closure returning the bundle version and can be used for unit testing and temporarily overriding the returned app version.

We defined a askForRatingIfNeeded() method that we can use in places we think it’s good to ask the user for a rating if the conditions are valid. It’s up to you to determine this moment but try to find a moment the user is not in his flow. For example, it would be harmful to ask for a rating right at the launch of your app since the user is likely looking for something.

To be even more sure you’re not blocking the user in their flow, you could delay asking for a rating by a few seconds.

let requestWorkItem = DispatchWorkItem {
    askForRatingIfNeeded()
}

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2), execute: requestWorkItem)

We should cancel the requestWorkItem whenever the user performs any user interaction. If the work item executes, we know that the user didn’t do anything for 2 seconds, increasing the chances they have time to submit a rating.

Setting up conditions to validate before asking

Besides asking for the right moment, it’s also good to set up conditions to validate before even executing the SKStoreReviewController requestReview method. I always like to set up the following conditions:

  • Only ask users that installed the app longer than seven days ago, making them returning users and increasing the chances they like the app.
  • Ask for a rating once per app version to prevent asking to review the same app version multiple times.
  • The user should have a minimum number of sessions.
  • Significant user events should’ve been triggered, indicating the user understands and uses the app.

The latter could be uploading a file in the WeTransfer app or creating a recording with RocketSim. These are significant events within those apps and indicate an active user.

All combined, a possible implementation could look as follows:

var shouldAskForRating: Bool {
    guard !isDebuggingEnabled else { return true }
    guard let firstLaunchDate = Defaults[.firstOpenDate] else { return false }
    let timeSinceFirstLaunch = Date().timeIntervalSince(firstLaunchDate)
    let timeUntilRate: TimeInterval = 60 * 60 * 24 * TimeInterval(configuration.daysUntilPrompt)

    return Defaults[.appSessionsCount] >= configuration.sessionsUntilPrompt
        && Defaults[.ratingEventsCount] >= configuration.eventsUntilPrompt
        && timeSinceFirstLaunch >= timeUntilRate
        && Defaults[.lastVersionPromptedForReview] != applicationVersionProvider()
}

I’m making use of this user defaults convenience library for the Defaults[] syntax.

The shouldAskForRating method returns true if debugging is enabled so you can ensure the popup shows up at the right moment. Note that the rating prompt will always show up when a debugger is attached.

I’ll leave it as an exercise for you to put all pieces together, but there are a few more hints I would like to share. Catching significant user events is done by this static method:

static func didPerformSignificantEvent() {
    Defaults[.ratingEventsCount] += 1
}

You can capture the sessions count and first launch date using this code:

@objc private func applicationDidBecomeActive(_ notification: Notification) {
    Defaults[.appSessionsCount] += 1

    if Defaults[.firstOpenDate] == nil {
        Defaults[.firstOpenDate] = Date()
    }
}

And a potential configuration struct looks as follows:

public struct RatingRequesterConfiguration {
    public typealias RatingRequestedHandler = ((String) -> Void)

    /// The minimum amounts of days until the first prompt should be shown.
    let daysUntilPrompt: Int

    /// The number of app sessions required until the first prompt should be shown.
    let sessionsUntilPrompt: Int

    /// The number of events required until the first prompt should be shown.
    let eventsUntilPrompt: Int

    /// Will be called after the user is requested for a review.
    let onRatingRequest: RatingRequestedHandler?

    public init(daysUntilPrompt: Int, sessionsUntilPrompt: Int, eventsUntilPrompt: Int, onRatingRequest: RatingRequesterConfiguration.RatingRequestedHandler?) {
        self.daysUntilPrompt = daysUntilPrompt
        self.sessionsUntilPrompt = sessionsUntilPrompt
        self.eventsUntilPrompt = eventsUntilPrompt
        self.onRatingRequest = onRatingRequest
    }
}

Validating your results

After implementing the right conditions, it’s good to ensure you’re validating the outcome of your SKStoreReviewController implementation.

  • Is the number of ratings you get increasing?
  • Do you get more positive ratings, or are your negative ratings increasing?

You could even implement tracking events to compare the number of received ratings to the number of times you requested a review.

We’re using AppFigures to validate our rating performance, and you can tell when we started using the technique from this article in the WeTransfer app:

WeTranfer's ratings increased a lot after asking users for ratings inside the app.
WeTranfer’s ratings increased after asking users for ratings inside the app.

How can I ask for reviews instead?

While ratings are already excellent, you might be looking to ask for reviews instead. You can use the following code to deeplink the user into the App Store to submit an actual review of your app:

@IBAction func requestReviewManually() {
    // Note: Replace the XXXXXXXXXX below with the App Store ID for your app
    //       You can find the App Store ID in your app's product URL
    guard let writeReviewURL = URL(string: "https://apps.apple.com/app/idXXXXXXXXXX?action=write-review")
        else { fatalError("Expected a valid URL") }
    UIApplication.shared.open(writeReviewURL, options: [:], completionHandler: nil)
}

A great technique can be to provide this option to users from their account screen or by creating a custom rating request popup that executes this method. However, the rating prompt is less intrusive and might result in better outcomes.

Conclusion

It is relatively easy to ask for ratings using the SKStoreReviewController, but it’s essential to create the right timing. Ratings help you for App Store Optimization and can result in more users installing your app. Set up conditions to ensure the user is not spammed with rating requests and increase the chances for positive reviews.

If you like to improve your Swift knowledge, check out the Swift category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!