Testing in-app purchase with the App Store Connect / Xcode configuration file allows testing of approximately 43 non-consumable product errors.
Testing my non-consumable in-app purchase with the new ProductView on my app for these various faults doses’t appear to be very promising.
Loading errors: all result in a blank screen and spinning wheel without producing an alert.
Purchase errors: Some of these work producing readable comments. Purchase (not entitled) and Purchase (system error) just produce alert boxes with an OK button. Purchase (network) produces the alert “The operation could not be completed. (NSURLErrorDomain error - 1009.)”
Verification errors, App Store Sync, and App Transaction errors: These purchases are successful and produce no alerts.
Am I missing a method that handles these errors? With ProductView do I need to use a do / catch block in my viewModel? Can I release this app for review working like this?
struct StoreView: View {
@StateObject private var store = StoreModel()
var body: some View {
GeometryReader { g in
VStack {
Spacer()
Text("\(mainTitle)")
.font(.title)
.padding(.bottom, 25)
ProductView(id: "xxxx") { _ in
Image(systemName: "xxxx")
.resizable()
.scaledToFit()
} placeholderIcon: {
ProgressView()
}
.productViewStyle(.large)
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(Color( "darkGreen"), lineWidth: 5)
.frame(width: g.size.width * 0.90, height: g.size.height * 0.35 )
)
.frame(maxWidth: .infinity, alignment: .center)
Spacer()
}
}
}
}
@MainActor final class StoreModel: ObservableObject {
@Published private(set) var products: [Product] = []
@Published private(set) var activeTransactions: Set<StoreKit.Transaction> = []
@Published var gotRefund = false // use to reset purchase
private var updates: Task<Void, Never>?
private var productIDs = ["xxxxx"]
// app wide purchase status
@AppStorage(StorageKeys.purStatus.rawValue) var storeStatus: Bool = false
init()
{
updates = Task {
for await update in StoreKit.Transaction.updates {
if let transaction = try? update.payloadValue {
await fetchActiveTransactions()
// check transaction.revocationDate for refund
if transaction.revocationDate != nil {
self.storeStatus = false // requesting refund
self.gotRefund = true
} else {
self.storeStatus = true // making purchase
}
await transaction.finish()
}
}
}
} // end init
deinit {
updates?.cancel()
}
// update the current entitlements
func fetchActiveTransactions() async {
var activeTransactions: Set<StoreKit.Transaction> = []
for await entitlement in StoreKit.Transaction.currentEntitlements {
if let transaction = try? entitlement.payloadValue {
activeTransactions.insert(transaction)
}
}
self.activeTransactions = activeTransactions
}
}