How to handle small delay of Transaction.currentEntitlements?

87 Views Asked by At

If I rely on Transaction.currentEntitlements to check if "Unlock more" was purchased, there is a small delay. This is noticeable when changing colors, e.g. .environment(\.colorScheme, store.isUnlocked ? .dark : .light). (this is in development mode, maybe it's faster once in production)

    @MainActor
    func updatePurchasedProducts() async {
        var purchasedProducts: [Product] = []
        
        for await result in Transaction.currentEntitlements {
            do {
                let transaction = try checkVerified(result)
                if let product = products.first(where: { $0.id == transaction.productID}) {
                    purchasedProducts.append(product)
                }
            } catch {
                print("Transaction failed verification", error)
            }
        }
        
        self.purchasedProducts = purchasedProducts
    }

    func isPurchased(productIdentifier: String) -> Bool {
        return purchasedProducts.contains { product in
            product.id == productIdentifier
        }
    }

Is it common practice to store var purchasedProducts: [Product] in @AppStorage?

Full code:

import Foundation
import StoreKit

let unlockMore = "bundleId.unlock_more"

class Store: ObservableObject {
    @Published var products: [Product] = []
    @Published var purchasedProducts: [Product] = []
    
    var updateListenerTask: Task<Void, Error>? = nil
    
    var isUnlocked: Bool {
        get {
            return isPurchased(productIdentifier: unlockMore)
        }
    }
    
    init() {
        updateListenerTask = listenForTransations()
        Task {
            await requestProducts()
            await updatePurchasedProducts()
        }
    }
    
    func listenForTransations() -> Task<Void, Error> {
        return Task.detached {
            for await result in Transaction.updates {
                do {
                    let transaction = try self.checkVerified(result)
                    await self.updatePurchasedProducts()
                    await transaction.finish()
                } catch {
                    print(error)
                }
            }
        }
    }
    
    @MainActor
    func requestProducts() async {
        do {
            let productIdentifiers = [unlockMore]
            products = try await Product.products(for: productIdentifiers)
        } catch {
            print(error)
        }
    }
    
    func purchase(product: Product) async throws -> Transaction? {
        let result = try await product.purchase()
        
        switch result {
        case .success(let verificationResult):
            let transaction = try checkVerified(verificationResult)
            await updatePurchasedProducts()
            await transaction.finish()
            return transaction
        case .userCancelled, .pending:
            return nil
        default:
            return nil
        }
    }
    
    func isPurchased(product: Product) -> Bool {
        return purchasedProducts.contains(product)
    }
    
    func isPurchased(productIdentifier: String) -> Bool {
        return purchasedProducts.contains { product in
            product.id == productIdentifier
        }
    }
   
    @MainActor
    func updatePurchasedProducts() async {
        var purchasedProducts: [Product] = []
        
        for await result in Transaction.currentEntitlements {
            do {
                let transaction = try checkVerified(result)
                if let product = products.first(where: { $0.id == transaction.productID}) {
                    purchasedProducts.append(product)
                }
            } catch {
                print("Transaction failed verification", error)
            }
        }
        
        self.purchasedProducts = purchasedProducts
    }
    
    func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .unverified:
            // failed verification
            throw StoreError.failedVerification
        case .verified(let signedType):
            return signedType
        }
    }
}

enum StoreError: Error {
    case failedVerification
}
0

There are 0 best solutions below