Search code examples
iosswiftstorekitstorekit2

How to properly handle auto-renewable with StoreKit 2?


I recently transitioned to StoreKit 2 to handle the active subscription within my app. But there is some aspect of it I'm not entirely sure. The documentation about the revocationDate and expiration date are not clear to me.

  1. If a user cancel his auto-renewable while being in a 7 day trial, will the revocation date be non nil?

  2. Does Transaction.currentEntitlements only contains currently active product? I'm assuming I don't really need to check if the expirationDate is expired or not if that's the case.

  3. Is it recommended to execute this code in the MainThread?

Here is the full method :

    @MainActor
    func syncActiveAppleProduct() async {
        LoggerManager.shared.warning("[Fetch Current Entitlements]")
        
        var activeSubscriptions: [Product] = []

        for await result in Transaction.currentEntitlements {
            do {

                let transaction = try checkVerified(result)
                
                guard let subscription: Product = subscriptions.first(where: { $0.id == transaction.productID }) else {
                    continue
                }
                
                switch transaction.productType {
                case .autoRenewable:
                    guard transaction.revocationDate == nil else { continue }

                    if let expirationDate = transaction.expirationDate {
                        KeychainHelper.Subscription.setExpiryOrCancelledDate(expirationDate)
                    }
                    activeSubscriptions.append(subscription)
                    
                case .consumable, .nonConsumable, .nonRenewable:
                    guard let expirationDate = getNonRenewingExpirationDate(forProductId: transaction.productID, purchaseDate: transaction.purchaseDate) else { continue }
                    if Date() < expirationDate {
                        KeychainHelper.Subscription.setExpiryOrCancelledDate(expirationDate)
                        activeSubscriptions.append(subscription)
                    }
                default:
                    continue
                }
                
            } catch let error {
                LoggerManager.shared.error(error: error, message: "Failed update active customer product.")
            }
        }

        self.activeSubscriptions = activeSubscriptions
    }

Solution

    1. if auto-renewable subscription is cancelled during trial period, revocationDate will be set when the trial period ends, because subscription was not technically active during this time.

    2. Yes, Transaction.currentEntitlements only includes currently active products. If the subscription is expired or cancelled, it will not be included in the returned list, but for non-renewing subscriptions, you may need to check the expiration date to determine if the user has access to the subscription benefits

    3. most of the storekit methods and callbacks are automatically executed on the main thread, so yes It is recommended to execute code in the main thread, this will also ensure there are not any potential threading issues