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
p 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 Extensiono370:89772] ]Purchases] - DEBUG: ℹ️ Debug logging enabled
2022-01-04 12:46:15.727275-0700 Next WatchKit Extensione370:89772] 9Purchases] - DEBUG: ℹ️ SDK Version - 3.13.1
2022-01-04 12:46:15.727348-0700 Next WatchKit Extension 370:89772] 7Purchases] - DEBUG: 👤 Initial App User ID - (null)
2022-01-04 12:46:16.034222-0700 Next WatchKit ExtensionK370:89772] nPurchases] - DEBUG: ℹ️ No cached Offerings, fetching from network
2022-01-04 12:46:16.095313-0700 Next WatchKit Extensiona370:89772] nPurchases] - DEBUG: ℹ️ PurchaserInfo cache is stale, updating from network in foreground.
2022-01-04 12:46:16.096663-0700 Next WatchKit Extensionx370:89772] EPurchases] - DEBUG: 😻 PurchaserInfo updated from network.
2022-01-04 12:46:16.105116-0700 Next WatchKit Extension 370:90003] iPurchases] - DEBUG: ℹ️ There are no requests currently running, starting request GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b
2022-01-04 12:46:16.105217-0700 Next WatchKit Extension0370:90003] tPurchases] - DEBUG: ℹ️ API request started: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b
2022-01-04 12:46:16.111246-0700 Next WatchKit Extension2370:89772] tscenes] 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 Extension1370:90003] 0Purchases] - 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] 0Purchases] - DEBUG: ℹ️ API request completed with status: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b 200
2022-01-04 12:46:17.584104-0700 Next WatchKit Extension4370:89772] 8scenes] 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 Extension4370:89995] 9Purchases] - DEBUG: ℹ️ Serial request done: GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b, 1 requests left in the queue
2022-01-04 12:46:17.592338-0700 Next WatchKit Extension0370:89995] 1Purchases] - 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] 1Purchases] - DEBUG: ℹ️ There are no requests currently running, starting request GET /subscribers/$RCAnonymousID0X0P+050359f9165194c4bbf1fbcbc5462399b/offerings
2022-01-04 12:46:17.595002-0700 Next WatchKit Extensionb370: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] 2Purchases] - DEBUG: ℹ️ API request completed with status: GET /v1/subscribers/$RCAnonymousID:50359f9165194c4bbf1fbcbc5462399b/offerings 200
2022-01-04 12:46:18.153605-0700 Next WatchKit Extension0370: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 Extensionb370: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 Extensionn370:90002] bPurchases] - DEBUG: ℹ️ Products request finished.
2022-01-04 12:46:19.285092-0700 Next WatchKit Extensionq370:90002] ePurchases] - DEBUG: 💰 Retrieved SKProducts:
2022-01-04 12:46:19.285612-0700 Next WatchKit Extensionr370:90002] dPurchases] - DEBUG: 💰 next_watchos_59.99_annually_1_month_free - <SKProduct: 0x15647a10>
2022-01-04 12:46:19.285824-0700 Next WatchKit Extensiono370:90002] 4Purchases] - DEBUG: ℹ️ 2 completion handlers waiting on products
2022-01-04 12:47:12.165478-0700 Next WatchKit Extensiond370:89772] gPurchases] - 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] ePurchases] - 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 Extensionm370: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 Extensione370: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 ExtensionN370:89772] ePurchases] - ERROR: 🍎‼️ There was a problem with the App Store.
2022-01-04 12:47:43.187083-0700 Next WatchKit ExtensionR370: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 Extensiono370:91940] hPurchases] - 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.allyself.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.allaself.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?