UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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>>"
)>

3      

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.

3      

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.

2      

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 :)

2      

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.

2      

Tested on Xcode 13.4.1. In my iPhone simulator test I have only one inactive. My workaround is

var scene = allScenes.first { $0.activationState == .foregroundActive }
if scene == nil {
    scene = allScenes.first
}

so if it's a simulator problem, I still get the correct one on the device. However in my iPad simulator, having 2 versions running side by side, I get two scenes in the list. Finally, not windows show the review window. Is that how it should be?

I would like to understand, what exactly is going wrong here.

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.