Question

Independent watchOS app, subscription purchase fails on device (Xcode and TestFlight)

  • 4 January 2022
  • 2 replies
  • 162 views

Badge

Context:

  • New, 1.0 release of an app
  • Independent watchOS app, e.g. no iOS app
  • Purchases work in sandbox/simulator and see them show up on the RevenueCat dashboard when viewing sandbox data
  • I’m using the Purchases framework version 3.13.1 with SPM

When I build and run on my physical Apple Watch and attempt to purchase, I get this failure message:

Unable to Purchase App

Sign in with your Apple ID from the Apple Watch app on your iPhone

[ OK ]

But I am already signed in with my personal Apple ID account AND have my Sandbox Account configured on my iPhone.

My logs look like with a fresh install/build from Xcode:

2022-01-04 12:46:15.726123-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: ℹ️ Debug logging enabled

2022-01-04 12:46:15.727275-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: ℹ️ SDK Version - 3.13.1

2022-01-04 12:46:15.727348-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: 👤 Initial App User ID - (null)

2022-01-04 12:46:16.034222-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: ℹ️ No cached Offerings, fetching from network

2022-01-04 12:46:16.095313-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: ℹ️ PurchaserInfo cache is stale, updating from network in foreground.

2022-01-04 12:46:16.096663-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: 😻 PurchaserInfo updated from network.

2022-01-04 12:46:16.105116-0700 Next WatchKit Extension[370:90003] [Purchases] - DEBUG: ℹ️ There are no requests currently running, starting request GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b

2022-01-04 12:46:16.105217-0700 Next WatchKit Extension[370:90003] [Purchases] - DEBUG: ℹ️ API request started: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b

2022-01-04 12:46:16.111246-0700 Next WatchKit Extension[370:89772] [scenes] unable to send specifiers:<__NSArrayM: 0x155652c0; count: 1> {

    <BLSAlwaysOnDateSpecifier: 0x155f8e30; date: 12:46:05.572; fidelity: Never>;

} response to frameSpecifiersAction:<BLSFrameSpecifiersRequestAction: 0x15679e10; info: <BSSettings: 0x15679e30> {

    (1) = <_NSConcreteDateInterval: 0x1567ac20> (Start Date) 2022-01-04 19:46:05 +0000 + (Duration) 840.000000 seconds = (End Date) 2022-01-04 20:00:05 +0000;

    (3) = BSSettingFlagYes;

}; responder: <_BSActionResponder: 0x15679520; active: YES; waiting: NO> clientInvalidated = NO;

clientEncoded = NO;

clientResponded = NO;

reply = <BSMachPortSendOnceRight: 0x1567ac30; usable: NO; (370:0:send-once xpcCode) from (45:0:send-once take)>;

annulled = YES;>

2022-01-04 12:46:16.112486-0700 Next WatchKit Extension[370:90003] [Purchases] - DEBUG: ℹ️ There's a request currently running and 0 requests left in the queue, queueing GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b/offerings

2022-01-04 12:46:17.568690-0700 Next WatchKit Extension[370:89995] [Purchases] - DEBUG: ℹ️ API request completed with status: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b 200

2022-01-04 12:46:17.584104-0700 Next WatchKit Extension[370:89772] [scenes] unable to send desiredFidelity:Never response to desiredFidelityAction:<BLSDesiredFidelityAction: 0x1567f4f0; info: 0x0; responder: <_BSActionResponder: 0x1567b5a0; active: YES; waiting: NO> clientInvalidated = NO;

clientEncoded = NO;

clientResponded = NO;

reply = <BSMachPortSendOnceRight: 0x156fa210; usable: NO; (370:0:send-once xpcCode) from (45:0:send-once take)>;

annulled = YES;>

2022-01-04 12:46:17.592104-0700 Next WatchKit Extension[370:89995] [Purchases] - DEBUG: ℹ️ Serial request done: GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b, 1 requests left in the queue

2022-01-04 12:46:17.592338-0700 Next WatchKit Extension[370:89995] [Purchases] - DEBUG: ℹ️ Starting the next request in the queue, <RCHTTPRequest: httpMethod=GET

path=/subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b/offerings

requestBody=(null)

headers={

    Authorization = "Bearer appl_tvUrSwzGWpZrCaOvFTTuMbCmfdR";

}

retried=0

>

2022-01-04 12:46:17.594933-0700 Next WatchKit Extension[370:89995] [Purchases] - DEBUG: ℹ️ There are no requests currently running, starting request GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b/offerings

2022-01-04 12:46:17.595002-0700 Next WatchKit Extension[370:89995] [Purchases] - DEBUG: ℹ️ API request started: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b/offerings

2022-01-04 12:46:18.147652-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: ℹ️ API request completed with status: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b/offerings 200

2022-01-04 12:46:18.153605-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: ℹ️ Requesting products from the store with identifiers: {(

    "next_watchos_59.99_annually_1_month_free"

)}

2022-01-04 12:46:18.154358-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: ℹ️ Serial request done: GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b/offerings, 0 requests left in the queue

2022-01-04 12:46:19.284917-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: ℹ️ Products request finished.

2022-01-04 12:46:19.285092-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: 💰 Retrieved SKProducts:

2022-01-04 12:46:19.285612-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: 💰 next_watchos_59.99_annually_1_month_free - <SKProduct: 0x15647a10>

2022-01-04 12:46:19.285824-0700 Next WatchKit Extension[370:90002] [Purchases] - DEBUG: ℹ️ 2 completion handlers waiting on products

2022-01-04 12:47:12.165478-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: ℹ️ makePurchase

2022-01-04 12:47:12.169755-0700 Next WatchKit Extension[370:89772] [Purchases] - DEBUG: 💰 Purchasing product from package  - next_watchos_59.99_annually_1_month_free in Offering default

2022-01-04 12:47:12.170124-0700 Next WatchKit Extension[370:89999] [Purchases] - DEBUG: ℹ️ PaymentQueue updatedTransaction: next_watchos_59.99_annually_1_month_free (null) ((null)) (null) - 0

2022-01-04 12:47:43.176784-0700 Next WatchKit Extension[370:91940] <SKPaymentQueue: 0x155e0210>: Payment completed with error: Error Domain=ASDErrorDomain Code=530 "The operation couldn’t be completed. (ASDErrorDomain error 530.)" UserInfo={NSLocalizedDescription=The operation couldn’t be completed. (ASDErrorDomain error 530.)}

2022-01-04 12:47:43.186559-0700 Next WatchKit Extension[370:91940] [Purchases] - DEBUG: ℹ️ PaymentQueue updatedTransaction: next_watchos_59.99_annually_1_month_free (null) (Error Domain=SKErrorDomain Code=0 "UNKNOWN_ERROR" UserInfo={NSUnderlyingError=0x15615a00 {Error Domain=ASDErrorDomain Code=530 "The operation couldn’t be completed. (ASDErrorDomain error 530.)" UserInfo={NSLocalizedDescription=The operation couldn’t be completed. (ASDErrorDomain error 530.)}}, NSLocalizedDescription=UNKNOWN_ERROR}) (null) - 2

2022-01-04 12:47:43.186925-0700 Next WatchKit Extension[370:89772] [Purchases] - ERROR: 🍎‼️ There was a problem with the App Store.

2022-01-04 12:47:43.187083-0700 Next WatchKit Extension[370:91940] [Purchases] - DEBUG: 💰 Finishing transaction next_watchos_59.99_annually_1_month_free (null) ((null))

2022-01-04 12:47:43.188266-0700 Next WatchKit Extension[370:91940] [Purchases] - DEBUG: ℹ️ PaymentQueue removedTransaction: next_watchos_59.99_annually_1_month_free (null) ((null) Error Domain=SKErrorDomain Code=0 "UNKNOWN_ERROR" UserInfo={NSUnderlyingError=0x15615a00 {Error Domain=ASDErrorDomain Code=530 "The operation couldn’t be completed. (ASDErrorDomain error 530.)" UserInfo={NSLocalizedDescription=The operation couldn’t be completed. (ASDErrorDomain error 530.)}}, NSLocalizedDescription=UNKNOWN_ERROR}) {

    NSLocalizedDescription = "UNKNOWN_ERROR";

    NSUnderlyingError = "Error Domain=ASDErrorDomain Code=530 \"The operation couldn\U2019t be completed. (ASDErrorDomain error 530.)\" UserInfo={NSLocalizedDescription=The operation couldn\U2019t be completed. (ASDErrorDomain error 530.)}";

} - 2

When I submit this app for App Store review it is rejected with this message:

We discovered one or more bugs in your app. Specifically, the subscription button was unresponsive. Please review the details below and complete the next steps.

Review device details:

- Device type: Watch

- OS version: watchOS 8.3

My subscription code feels very basic/simple to me, but including incase I’m missing something very obvious:

class SubscriptionManager: ObservableObject {

    let entitlementIdentifier = "unlimited"

    

    @Published private(set) var isSubscribed: Bool = false

    

    var currentOffering: Purchases.Offering? = nil

    

    func verifySubscriptionStatus() {

        Purchases.shared.purchaserInfo { (purchaserInfo, error) in

            if purchaserInfo?.entitlements.all[self.entitlementIdentifier]?.isActive == true {

                self.isSubscribed = true

            }

        }

    }

    

    func purchaseSubscription() {

        if let package = currentOffering?.availablePackages.first {

            Purchases.shared.purchasePackage(package) { (transaction, purchaserInfo, error, userCancelled) in

                if purchaserInfo?.entitlements.all[self.entitlementIdentifier]?.isActive == true {

                    self.isSubscribed = true

                }

            }

        }

    }

    

    func restorePurchases() {

        Purchases.shared.restoreTransactions { (purchaserInfo, error) in

            if error != nil {

                // do something with the error at some point...

            } else {

                self.isSubscribed = true

            }

        }

    }

    

    func fetchOfferings() {

        Purchases.shared.offerings { (offerings, error) in

            if let error = error {

                print(error.localizedDescription)

            }

            self.currentOffering = offerings?.current

        }

    }

    

    init() {

        verifySubscriptionStatus()

        fetchOfferings()

    }

}

Any ideas what might be causing Purchases to fail on my Apple Watch when running from Xcode or TestFlight?


2 replies

Userlevel 5
Badge +9

Your code looks fine, the dreaded UNKNOWN_ERROR from Apple is always a tricky one to troubleshoot, but I bet it’s related to that first screenshot of not being able to sign in.

From the logs the products are able to fetch fine, so there is some connection to StoreKit happening there…

Sometimes searching for the ASDError will pop up some helpful results. Unfortunately Apple has 0 documentation on the ASDErrorDomain namespace or how to handle it so there’s no “official” way to resolve the error other than the Unknown Error Docs.

When this error occurs during testing, it can often be resolved by logging out of iTunes and/or the App Store and creating a new test user account in App Store Connect. This approach only works when running the app on a device, not the simulator.

When this error occurs in production, it may indicate a problem with the user’s iTunes account.

 

A little strange I only see 1 other post on the internet with the specific ASDErrorDomain Code=530https://stackoverflow.com/questions/66560647/skreceiptrefreshrequest-failed-with-asderrordomain-code-530. The one answer there feels a little voodoo to me, but does point back to some issue with the sandbox/testflight account. 

I would try creating a new sandbox account and signing in with that. For some folks it looks like some apps being updated in the background were causing a similar issue: https://discussions.apple.com/thread/250668295

 

I have the same very problem (has to sign in with your appleID) and my app is also stuck in review with the same very rejection reason.

It seems to me that something has changed (is broken) on the Apple side, since I already have an Apple Watch only app in store and purchases in sandbox environment via RevenueCat also stopped working for it (although everything still works fine in production). And purchases did work before.

What I’ve done:

  1. Created a video of purchases working in Xcode using local storeKit file
  2. Made some errors handling and display

Will try to submit all that including link to this thread and let’s hope it will work out.

If not, I’ll file a bug.

Reply