On an app that was using the old API for In-App Purchases (StoreKit 1). The app is already published on the App Store. The purchase is non-consumable.
While trying to migrate to StoreKit 2, I'm unable to restore purchases. Specifically displaying and purchasing products works as expected, but when deleting and reinstalling the app, and then trying to restore purchases I can't do it.
I'm trying to restore them using the new APIs but it doesn't seem to be working. What I have tried so far:
I'm listening for transaction updates during the whole lifetime of the app, with:
Task.detached {
for await result in Transaction.updates {
if case let .verified(safe) = result {
}
}
}
I have a button that calls this method, but other than prompting to log in again with the Apple ID it doesn't seem to have any effect at all:
try? await AppStore.sync()
This doesn't return any item
for await result in Transaction.currentEntitlements {
if case let .verified(transaction) = result {
}
}
This doesn't return any item
for await result in Transaction.all {
if case let .verified(transaction) = result {
}
}
As mentioned before I'm trying this after purchasing the item and deleting the app. So I'm sure it should be able to restore the purchase. Am trying this both with a Configuration.storekit file on the simulator, and without it on a real device, in the Sandbox Environment.
Has anyone being able to restore purchases using StoreKit 2?
Ps: I already filed a feedback report on Feedback Assistant, but so far the only thing that they have replied is:
Because StoreKit Testing in Xcode is a local environment, and the data is tied to the app, when you delete the app you're also deleting all the transaction data for that app in the Xcode environment. The code snippets provided are correct usage of the API.
So yes, using a Configuration.storekit file won't work on restoring purchases, but if I can't restore them on the Sandbox Environment I'm afraid that this won't work once released, leaving my users totally unable to restore what they have already purchased.
On iOS 17 we have a new API to take care of this integrated in SwiftUI
currentEntitlementTask(for:priority:action:)
It handles this much more easily and with just a few lines of code. I can also validate that it works both on Release and Debug mode.
import SwiftUI
import StoreKit
struct MyView: View {
var body: some View {
VStack {
/// your view
}
.currentEntitlementTask(for: "My Purchase ID") { state in
guard
let transaction = state.transaction,
case .verified(let verified) = transaction
else { return }
/// ...
}
}
}
Remember to import StoreKit
to be able to use this View Modifier
.
After releasing to the App Store and finally trying the app directly in production I can confirm that it works, but I have to say that it is not ideal to be unable to test this on the sandbox environment.
Also I feel the documentation was not clear enough, at least not for me.
Probably it is clear for other folks, but I was expecting the purchases to be restored automatically and get them on for await result in Transaction.updates
, but this didn't work.
What did work was to check Transaction.currentEntitlements
, and if the entitlement is not there, then do a sync()
and check again.
This is the code that worked for me:
try? await AppStore.sync()
for await result in Transaction.currentEntitlements {
if case let .verified(transaction) = result {
// ...
}
}
release mode
and doesn't work on debug mode
without StoreKit Testing, that is without a Configuration.storekit
.Configuration.storekit
) restoring purchases works (with this same approach), but only if you don't delete de app and reinstall again. By deleting the app you loose StoreKit Testing history.