Simple question: How to ensure that a communication between an iOS application and a back-end succeeded?
Detail: For most of the API call in our mobile application, it can be "OK" if the communication between the application and the server fails (for network reason or other), we can just display a message to the user with a retry button to reload his news or posts feed for instance.
However, on some occasion, we want to be really confident that the communication between the application and the backend will never fail and the data of this communication will potentially be "lost".
For this, let's take as an example, the In-App Purchases (IAP):
struct InAppPurchase{
// The id of the purchase
let transactionId: String
// The id of the user who purchased
let userId: String
// The IAP product involved in the purchase (10 or 30 in-app coins for example)
let productId: String
}
I was thinking of this approach:
UserDefault
or Keychain
like so var pendingPurchases = [InAppPurchase]()
When the user purchases an item, this item is store in the pendingPurchases
array.
The application communicates with the back-end by sending this array of IAP
If I receive the answer code 200 SUCCESS
from the back-end, I can purge the pendingPurchases
array.
If I receive another code for an ERROR, I try to send this call another time (limited to 3 in a row, if it is because of bad network, it maybe not needed to try sending the request indefinitely at the moment) until receiving 200 SUCCESS
Each time the application is open or switch from background to foreground, I check if the pendingPurchases
is empty or not. If not empty, I send the request to the server.
What do you think of this approach? How do you manage this kind of data that you don't want to be "lost"?
What you want to do is wait to "finish the transaction" until you've received a 200
response from your server. If you don't finish the transaction, Apple will save it in a payment queue and keep sending it to you every time the app launches. This will give you an opportunity to handle any retries to your server if needed.
Finishing a transaction tells StoreKit that you’ve completed everything needed for the purchase. Unfinished transactions remain in the queue until they’re finished, and the transaction queue observer is called every time your app is launched so your app can finish the transactions. Your app needs to finish every transaction, regardless of whether the transaction succeeded or failed.
Complete all of the following actions before you finish the transaction:
- Persist the purchase.
- Download associated content.
- Update your app’s UI to let the user access the product.
To finish a transaction, call the finishTransaction: method on the payment queue.
SKPaymentTransaction *transaction = <# The current payment #>;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
You can read more about finishing transactions in Apple's doc below: https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/DeliverProduct.html