WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Issue with SKStoreKitReviewController on HWS+

Forums > SwiftUI

I am currently having an issue getting the correct activation state on app launch to show the SK Store Review Controller. So I call it in onAppear:

import SwiftUI

@main
struct UltimatePortfolio3App: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var dataController: DataController
    @StateObject var unlockManager: UnlockManager

    init() {
        let dataController = DataController()
        let unlockManager = UnlockManager(dataController: dataController)

        _dataController = StateObject(wrappedValue: dataController)
        _unlockManager = StateObject(wrappedValue: unlockManager)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, dataController.container.viewContext)
                .environmentObject(dataController)
                .environmentObject(unlockManager)
                .onReceive(
                    // Automatically save when we detect that we are no longer
                    // the foreground app. User this rather than the scene phase
                    // API so we can port to macOS, where scene phase won't detect
                    // our app losing focus as of macOS 11.1.
                    NotificationCenter.default.publisher(
                        for: UIApplication.willResignActiveNotification
                    ),
                    perform: save
                )
                .onAppear(perform: dataController.appLaunched)
        }
    }

    func save(_ note: Notification) {
        dataController.save()
    }
}

Then in the appLaunched method:

import Foundation
import StoreKit

extension DataController {
    func appLaunched() {
        guard count(for: Project.fetchRequest()) >= 5 else { return }

        let allScenes = UIApplication.shared.connectedScenes
        let scene = allScenes.first { $0.activationState == .foregroundActive }

        if let windowScene = scene as? UIWindowScene {
            SKStoreReviewController.requestReview(in: windowScene)
        }
    }
}

The issue is that the only scene I get back in the Set of connectedScenes has an activationState of UISceneActivationStateForegroundInactive, meaning the scene constant and windowScene are therefore nil, meaning there is no windowScene to pass into the requestReview. I am running iOS 15.4 and my target deployment is iOS 15.2 in Xcode 13.3.

(lldb) po allScenes.first
▿ Optional<UIScene>
  - some : <UIWindowScene: 0x13f1046e0; scene = <FBSScene: 0x2802b2f00; identifier: sceneID:com.davidhughes.UltimatePortfolio6-CF060879-F738-43EC-A7D2-01BF4C7D769C>; persistentIdentifier = CF060879-F738-43EC-A7D2-01BF4C7D769C; activationState = UISceneActivationStateForegroundInactive; settingsScene = <UIWindowScene: 0x13f1046e0>; windows = (
    "<UIWindow: 0x13dd11270; frame = (0 0; 428 926); gestureRecognizers = <NSArray: 0x2822ebc30>; layer = <UIWindowLayer: 0x2822eb9c0>>"
)>

1      

Same issue for me and also tried with Xcode 13.2.1 (and I think iOS 15.2). I was able to get the request to show up if I just used .first without the filtering closure.

1      

While your suggest works, I thought the idea of checking whether the screen is active is to prevent the view trying to show if the view is not active. It seems in a recent version of iOS Apple has changed how the scene is active.

   

I'm having the same issue (also XCode 13.3, targeting 15.0)

For learning purposes removing the filter works, but I'd like to know how to do this in a live app. Probably the absolutely worst place for your app to crash is when requesting a review :)

   

With a quick check I'm finding that there is only one scene found in the scope of appLaunched() and it has .activationState set to .foregroundInactive. If I open up two side-by-side instances on an iPad simulator I still only have one scene in scope (two unique total but only one visible within each instance). Both have the .foregroundInactive state. I don't see why this would be different in a production app through the app store versus just testing like this although I am admittedly not an expert so I would feel fine sending this out in an App Store version. Plus, we use if let right after that so the worse thing that can happen is not asking for a review.

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.