Below is Apple's stock Request Refund Logic from the Food Truck Building a SwiftUI Multiplatform App that I am trying to modify to catch refund request errors. Apple's doesn't seem to think that it is important to show how to catch the errors associated with their sample logic making it difficult for people like me that are still learning how to handle errors.
I am seeing the errors below in result where the comment says catch refund errors here. The one case statement dismisses control if the refund was successful.
This is what I am seeing with my various print statements:
refund error Binding<Optional>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.StoredLocation<Swift.Optional<Swift.Error>>, _value: nil) failure(StoreKit.Transaction.RefundRequestError.duplicateRequest) Transaction.RefundError RefundRequestError
So my question is how to get a localizedDescription of the error (in this case duplicateRequest) into the alert?
Part of the problem seems to be that the errors are presented after the successful case branches off without using a do / catch block. I have tried using error to store and use in the alert and gotError but I don't know the result type.
Thanks for your assistance!
struct RefundView: View {
@State private var recentTransactions: [StoreKit.Transaction] = []
@State private var selectedTransactionID: UInt64?
@State private var refundSheetIsPresented = false
@State private var error: Error?
@State private var refundError: Bool = false
@State var gotError: String = ""
@Environment(\.dismiss) private var dismiss
var body: some View {
List(recentTransactions, selection: $selectedTransactionID) { transaction in
TransactionRowView(transaction: transaction)
}
.safeAreaInset(edge: .bottom) {
VStack(spacing: 0) {
Text("Select a purchase to refund")
.font(.subheadline)
.foregroundColor(.secondary)
.padding()
Button {
refundSheetIsPresented = true
} label: {
Text("Request a refund")
.bold()
.padding(.vertical, 5)
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.padding([.horizontal, .bottom])
.disabled(selectedTransactionID == nil)
}
}
.task { @MainActor in
for await transaction in StoreKit.Transaction.all {
recentTransactions.append(transaction.unsafePayloadValue)
}
}
.refundRequestSheet(
for: selectedTransactionID ?? 0,
isPresented: $refundSheetIsPresented
) { result in
if case .success(.success) = result {
dismiss()
}
// ******* catch refund errors here *******
self.error = error
refundError.toggle()
// $gotError = result
print("refund error \($error)")
print(result)
print("Transaction.RefundError \(Transaction.RefundRequestError)")
}
.navigationTitle("Refund Purchase")
.alert("Refund error: \(gotError)", isPresented: $refundError, actions: {})
}
}
The
resultin this case isResult<Transaction.RefundRequestStatus, Transaction.RefundRequestError>and yourgotErrorvariable is aString, so it's not equivalent. You also don't need the prefix$. This symbol is for read-write binding.By writing like above, you're saying that in any case: success or failure, the
refundErrorproperty will always toggle, and that leads to:You can fix it by breaking
resultinto two cases, as @lorem said in his comment. Then changerefundError = trueonly, since it's a @State object and was linked with.alert(isPresented:). It will automatically return tofalsewhen the alert is dismissed.Finally, you don't have to use another
@State var gotError: Stringhere because you already storederror. So, I recommend using computed property to get the error localized description.