swiftuiin-app-purchaseios17

In-App Purchase Product Error Reporting Block not recognizing StoreKitError


I am trying to setup an in-app purchase for a non-consumable product with StoreKit 2. If you look at the App Store Connect / Xcode configuration file for in-app purchase there are 43 error conditions that Apple suggest testing before submitting an app for review. The problem is that all online sample code (including Apple’s) never show how to setup the code required to support these errors.

Specifically I am seeking help with load product errors. Under Apple’s discussion of products(for:) it says: If any identifiers are invalid or the App Store can’t find them, the App Store excludes them from the return value. The products(for:) function can throw a StoreKitError for system-related errors.

In my testing of load product errors I get an empty screen with a spinning wheel (progressView()) for all of these errors.

I am currently getting all kinds of errors in my product catch block switch statement such as Type '[Product]' has no member 'systemError'. It doesn't seem to recognize my StoreKitError enum. In the code below I'm not showing all of my StoreModel. I would be glad to show more if requested.


@MainActor final class StoreModel: ObservableObject {

enum StoreKitError {
        case networkError(URLError)
        case systemError(any Error)
        case userCancelled
        case notAvailableInStorefront
        case notEntitled
        case unknown
    }

public enum PurchaseFinishedAction {
        case dismissStore
        case noAction
        case displayError
    }

  func fetchProducts() async throws -> PurchaseFinishedAction {
        let action: PurchaseFinishedAction
        do {
            products = try await Product.products(
                for: productIDs)
        } catch {
            
            switch products {
            case .networkError(URLError):
                action = .displayError
            case .systemError(any Error):
                action = .displayError
            case .userCancelled:
                action = .noAction
            case .notAvailableInStorefront:
                action = .displayError
            case .notEntitled:
                action = .displayError
            case .product.unknown:
                action = .displayError
            }
        }
        return action
    }
       

Solution

  • Note, I'm not familliar with StoreKit, but this example code compiles for me. Note also StoreKit has already a StoreKitError no need to redeclare them, not sure about PurchaseFinishedAction

    @MainActor final class StoreModel: ObservableObject {
        
        @Published var products: [Product] = []  // <--- here
    
        public enum PurchaseFinishedAction {
            case dismissStore
            case noAction
            case displayError
            case noStoreKitError  // <--- here
        }
        
        func fetchProducts() async throws -> PurchaseFinishedAction {
            var action = PurchaseFinishedAction.noStoreKitError // <--- here
            do {
                products = try await Product.products(for: ["com.example.productA"]) // productIDs
            } catch {
                if let err = error as? StoreKitError { // <--- here
                    switch err {
                    case .networkError(let urlError):
                        action = .displayError
                    case .systemError(let sysError):
                        action = .displayError
                    case .userCancelled:
                        action = .noAction
                    case .notAvailableInStorefront:
                        action = .displayError
                    case .notEntitled:
                        action = .displayError
                    case .unknown:
                        action = .displayError
                    default:
                        action = .displayError  // <--- here, todo
                    }
                }
            }
            return action
        }
        
    }