Search code examples
iosswiftswiftuistorekitswiftystorekit

"SwiftyStoreKit.completeTransactions() should only be called once when the app launches."


I am using SwiftyStoreKit with SwiftUI and App instead of App Delegate and getting this message in the console: "SwiftyStoreKit.completeTransactions() should only be called once when the app launches." I believe it because I am calling completeTransactions in the below block which I can see from the Print statement gets called more often than previously AppDelegate's didFinishLaunching was called. The reason I am paying attention to this message is I got rejected from App Review for a crash when attempting to purchase. Where else should I call completeTransactions?

     .onChange(of: scenePhase) { newScenePhase in
                switch newScenePhase {
                case .active:
                    print("App is active")

  SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
            
            for purchase in purchases {
                switch purchase.transaction.transactionState {
                case .purchased, .restored:
                    let downloads = purchase.transaction.downloads
                    if !downloads.isEmpty {
                        SwiftyStoreKit.start(downloads)
                    } else if purchase.needsFinishTransaction {
                        // Deliver content from server, then:
                        SwiftyStoreKit.finishTransaction(purchase.transaction)
                    }
                    print("\(purchase.transaction.transactionState.debugDescription): \(purchase.productId)")
                case .failed, .purchasing, .deferred:
                    break // do nothing
                @unknown default:
                    break // do nothing
                }
            }
        }
                    

Solution

  • Try to do this in app main init, like

    @main
    struct MyApp: App {
    
        init() {
            SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
               // ... other code here
            }
        }
    
        var body: some Scene {
            WindowGroup {
               ContentView()
            }
        }
    }
    

    Note: if it is too early then you can define app delegate adaptor (like here https://stackoverflow.com/a/62538373/12299030) and call SwiftyStoreKit on did finish launching callback.