7

My intention is to either display the SKStore​Review​Controller (If applicable) or display my own feedback controller and redirect the user to the App Store. By doing this I can avoid asking the user for feedback more than once.

After reading Apple's lacking documentation on SKStore​Review​Controller (https://developer.apple.com/reference/storekit/skstorereviewcontroller) it seems that there is no way to determine if SKStore​Review​Controller is currently presented or has been presented previously.

I understand that I could potentially store the display frequency in NSUserDefaults, but I would prefer to avoid doing so.

Lachlan
  • 312
  • 2
  • 15
  • You say that you don't want to use UserDefaults but even if there is a way to determine if SKStore​Review​Controller is currently presented or has been presented previously then how would you store the value then? But I would check [this](http://stackoverflow.com/questions/43075515/how-to-use-requestreview-skstorereviewcontroller-to-show-review-popup-in-the) answer out. – Tarvo Mäesepp Apr 07 '17 at 01:19
  • Possible duplicate of [SKStoreReviewController how to detect that user has turned off Rate This App (RTA) in settings or 3 times limit has reached?](https://stackoverflow.com/questions/42533520/skstorereviewcontroller-how-to-detect-that-user-has-turned-off-rate-this-app-rt) – Nikola Lajic Sep 21 '17 at 20:23

1 Answers1

1

Here is how I detect if it has been presented.

private static func checkIfShownSKStoreReviewController(_ iteration: Int, originalWindowCount: Int) {
    let windows = UIApplication.shared.windows
    if windows.count > originalWindowCount {
        let window = windows[1]

        if window.className == "UITextEffectsWindow" || window.className == "UIRemoteKeyboardWindow" {
            print("Shown SKVC iteration: \(iteration)")

            //Do logic stuff like saving to your database
            return
        }
    }

    if iteration > 2000 {
        print("checkIfShownSKStoreReviewController: timeout, bailing \(iteration)")
        return
    }

    runThisAfterDelay(seconds: 0.02, after: {
        checkIfShownSKStoreReviewController(iteration + 1, originalWindowCount: originalWindowCount)
    })
}

private static func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue(), after)
}

static func showReview() {
    print("Showing AppStore Review")
    if #available(iOS 10.3, *) {
        SKStoreReviewController.requestReview()
        checkIfShownSKStoreReviewController(0, originalWindowCount: UIApplication.shared.windows.count)
    }
}
Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
  • Thanks for the answer. Although it will theoretically work, it's not exactly what I am looking for. It's not ideal to potentially wait 40 seconds then update the UI with an alternative feedback/review controller. Although in saying that, it's the best response I have seen thus far and it should eventually determine if the view has been displayed (Assuming it will be presented within the 40 seconds) – Lachlan Jul 27 '17 at 04:24
  • 1
    Well in my tries it usually presented in the 2-10th iteration. So 0.2 seconds max. But I'm sure it will vary by different factors. You could play with the iteration count and send a message after 5 seconds if you like. – Esqarrouth Jul 27 '17 at 11:45