Summary
The error state is one of the five UI states. It is pretty important in regards to the user’s experience and satisfaction. If you don’t handle errors probably, they can quickly become the main reason why users lose control of the app, and as a consequence, lead to a reduction in retention or even deletion of the app.
Error handling is more important when you work with dependencies, especially when working with a library like StoreKit. In general, StoreKit is a black-box in which anything can happen. In this short article, we will explore how to handle failed transactions and give feedback to the user with appropriate error messages.
![](https://framerusercontent.com/images/Lm1h5AfgilFZog1v7Q411tGGbMs.jpg)
![Handling StoreKit Errors](https://framerusercontent.com/images/BF8PX4EhMo3uh1FNplZaE0hpo4.jpg)
NSError Overview
Variety of SKErrors
Errors associated with payments, store products, and cloud services occur under the domain model SKErrorDomain
and error codes SKError.Code
. The entire list of errors includes more than 15 options, you can find them here below and in the documentation. In addition to SKErrorDomain
errors, there may be network problems that occur in the NSURLErrorDomain
model. Let’s take a look at how to handle them all.
There are a lot of types of errors, so I‘d recommend dividing them into several groups:
external, which cannot be influenced in any way, for example, network problems
request errors such as incorrect product ID
user side errors such as canceling an operation or an unverified account
Handling StoreKit errors
For error handling, we need to implement two methods of the SKRequestDelegate
delegate.
optional func request(_ request: SKRequest, didFailWithError error: Error)
Copy
This is an optional method that receives errors from SKRequest
. It is important to keep in mind that when these errors occur, (void) requestDidFinish: (SKRequest *) request
will not be called.
The second point of failure is transaction processing, here errors are quite varied and can be associated with both when the user canceled the operation, and when rights have been restricted, such as parental controls. In order to be able to receive errors when working with transactions, it is necessary to process the the transaction status SKPaymentTransactionStateFailed
separately. To get these events you should have a skpaymenttransactionobserver
.
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing: break; case SKPaymentTransactionStatePurchased: // implement handling purchased state // [self handlePurchasedTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self handleFailedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: // implement handling restored state // [self handlePurchasedTransaction:transaction]; break; default: break; } } }
Copy
You should handle paymentQueue(_:updatedTransactions:)
and all states of these transactions.
However, in this article we will dive deep into .failed
states. As Apple mentions in their official documentation, you should get the error
field from this transaction and then check the list of SKErrorDomain errors.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .failed: handleFailedTransaction(transaction) case ... } } }
Copy
How to handle SKErrors:
func handleFailedTransaction(_ transaction: SKPaymentTransaction) { guard let error = transaction.error as? SKError else { return } switch error.code { case ...: } }
Copy
Let’s take a look at all the possible cases of StoreKit errors.
.unknownSKErrorUnknown code 0
This error SKErrordomain 0 return usually happens when an unknown or unexpected error has occurred. Usually, there is no need to take any action on the developers side, but here are a few recommendations to check:
Check the LocalizedDescription property of the error object.
If the error appeared during the testing, try logging out or try it with a new test user.
The problem might be on the user’s account side, so it is worth asking the users to check and see if anyone gets back to you with more information.
.SKErrorClientInvalid code 1
This error is displayed when someone without the required permissions tries to perform the action. No action is needed to be done on your side. However, it is possible to try and show an automated notification with a message related to the action such as: “due to …. you are not allowed to perform the action. Please, change your account or device”.
.SKErrorPaymentCancelled code 2
This error code indicates that the user canceled a payment request. No action is needed from your side, however, you could try to look at the user’s behavior to find the right trigger to encourage the user to complete the payment in the future – such as an offer or triggered email that talks about the value of your app. The best reaction to this error would be to catch the event and send the user an automated notification with a discount offer or something else of value to try and encourage them to continue using your app.
.SKErrorPaymentInvalid code 3
This error shows that the payment was not processed correctly due to an issue with the billing – Error code indicating that one of the payment parameters wasn’t recognized by the App Store, card expiration or not enough funds. Try to check whether the billing issue is related to card expiration and if so, the best idea is to catch this error, and send an automated notification or email with a reminder to your user.
.SKErrorPaymentNotAllowed code 4
An error code that indicates that the user is not allowed to authorize payments.
.SKErrorStoreProductNotAvailable code 5
This error might happen when your user tries to purchase a product that is unavailable in their region. Check SKStorefront.
.SKErrorCloudServicePermissionDenied code 6
Error code that indicates that the user has not allowed access to Cloud service information.
The error state is one of the five UI states. It is pretty important in regards to the user’s experience and satisfaction. If you don’t handle errors probably, they can quickly become the main reason why users lose control of the app, and as a consequence, lead to a reduction in retention or even deletion of the app.
Error handling is more important when you work with dependencies, especially when working with a library like StoreKit. In general, StoreKit is a black-box in which anything can happen. In this short article, we will explore how to handle failed transactions and give feedback to the user with appropriate error messages.
![](https://framerusercontent.com/images/Lm1h5AfgilFZog1v7Q411tGGbMs.jpg)
![Handling StoreKit Errors](https://framerusercontent.com/images/BF8PX4EhMo3uh1FNplZaE0hpo4.jpg)
NSError Overview
Variety of SKErrors
Errors associated with payments, store products, and cloud services occur under the domain model SKErrorDomain
and error codes SKError.Code
. The entire list of errors includes more than 15 options, you can find them here below and in the documentation. In addition to SKErrorDomain
errors, there may be network problems that occur in the NSURLErrorDomain
model. Let’s take a look at how to handle them all.
There are a lot of types of errors, so I‘d recommend dividing them into several groups:
external, which cannot be influenced in any way, for example, network problems
request errors such as incorrect product ID
user side errors such as canceling an operation or an unverified account
Handling StoreKit errors
For error handling, we need to implement two methods of the SKRequestDelegate
delegate.
optional func request(_ request: SKRequest, didFailWithError error: Error)
Copy
This is an optional method that receives errors from SKRequest
. It is important to keep in mind that when these errors occur, (void) requestDidFinish: (SKRequest *) request
will not be called.
The second point of failure is transaction processing, here errors are quite varied and can be associated with both when the user canceled the operation, and when rights have been restricted, such as parental controls. In order to be able to receive errors when working with transactions, it is necessary to process the the transaction status SKPaymentTransactionStateFailed
separately. To get these events you should have a skpaymenttransactionobserver
.
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing: break; case SKPaymentTransactionStatePurchased: // implement handling purchased state // [self handlePurchasedTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self handleFailedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: // implement handling restored state // [self handlePurchasedTransaction:transaction]; break; default: break; } } }
Copy
You should handle paymentQueue(_:updatedTransactions:)
and all states of these transactions.
However, in this article we will dive deep into .failed
states. As Apple mentions in their official documentation, you should get the error
field from this transaction and then check the list of SKErrorDomain errors.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .failed: handleFailedTransaction(transaction) case ... } } }
Copy
How to handle SKErrors:
func handleFailedTransaction(_ transaction: SKPaymentTransaction) { guard let error = transaction.error as? SKError else { return } switch error.code { case ...: } }
Copy
Let’s take a look at all the possible cases of StoreKit errors.
.unknownSKErrorUnknown code 0
This error SKErrordomain 0 return usually happens when an unknown or unexpected error has occurred. Usually, there is no need to take any action on the developers side, but here are a few recommendations to check:
Check the LocalizedDescription property of the error object.
If the error appeared during the testing, try logging out or try it with a new test user.
The problem might be on the user’s account side, so it is worth asking the users to check and see if anyone gets back to you with more information.
.SKErrorClientInvalid code 1
This error is displayed when someone without the required permissions tries to perform the action. No action is needed to be done on your side. However, it is possible to try and show an automated notification with a message related to the action such as: “due to …. you are not allowed to perform the action. Please, change your account or device”.
.SKErrorPaymentCancelled code 2
This error code indicates that the user canceled a payment request. No action is needed from your side, however, you could try to look at the user’s behavior to find the right trigger to encourage the user to complete the payment in the future – such as an offer or triggered email that talks about the value of your app. The best reaction to this error would be to catch the event and send the user an automated notification with a discount offer or something else of value to try and encourage them to continue using your app.
.SKErrorPaymentInvalid code 3
This error shows that the payment was not processed correctly due to an issue with the billing – Error code indicating that one of the payment parameters wasn’t recognized by the App Store, card expiration or not enough funds. Try to check whether the billing issue is related to card expiration and if so, the best idea is to catch this error, and send an automated notification or email with a reminder to your user.
.SKErrorPaymentNotAllowed code 4
An error code that indicates that the user is not allowed to authorize payments.
.SKErrorStoreProductNotAvailable code 5
This error might happen when your user tries to purchase a product that is unavailable in their region. Check SKStorefront.
.SKErrorCloudServicePermissionDenied code 6
Error code that indicates that the user has not allowed access to Cloud service information.
The error state is one of the five UI states. It is pretty important in regards to the user’s experience and satisfaction. If you don’t handle errors probably, they can quickly become the main reason why users lose control of the app, and as a consequence, lead to a reduction in retention or even deletion of the app.
Error handling is more important when you work with dependencies, especially when working with a library like StoreKit. In general, StoreKit is a black-box in which anything can happen. In this short article, we will explore how to handle failed transactions and give feedback to the user with appropriate error messages.
![](https://framerusercontent.com/images/Lm1h5AfgilFZog1v7Q411tGGbMs.jpg)
![Handling StoreKit Errors](https://framerusercontent.com/images/BF8PX4EhMo3uh1FNplZaE0hpo4.jpg)
NSError Overview
Variety of SKErrors
Errors associated with payments, store products, and cloud services occur under the domain model SKErrorDomain
and error codes SKError.Code
. The entire list of errors includes more than 15 options, you can find them here below and in the documentation. In addition to SKErrorDomain
errors, there may be network problems that occur in the NSURLErrorDomain
model. Let’s take a look at how to handle them all.
There are a lot of types of errors, so I‘d recommend dividing them into several groups:
external, which cannot be influenced in any way, for example, network problems
request errors such as incorrect product ID
user side errors such as canceling an operation or an unverified account
Handling StoreKit errors
For error handling, we need to implement two methods of the SKRequestDelegate
delegate.
optional func request(_ request: SKRequest, didFailWithError error: Error)
Copy
This is an optional method that receives errors from SKRequest
. It is important to keep in mind that when these errors occur, (void) requestDidFinish: (SKRequest *) request
will not be called.
The second point of failure is transaction processing, here errors are quite varied and can be associated with both when the user canceled the operation, and when rights have been restricted, such as parental controls. In order to be able to receive errors when working with transactions, it is necessary to process the the transaction status SKPaymentTransactionStateFailed
separately. To get these events you should have a skpaymenttransactionobserver
.
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing: break; case SKPaymentTransactionStatePurchased: // implement handling purchased state // [self handlePurchasedTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self handleFailedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: // implement handling restored state // [self handlePurchasedTransaction:transaction]; break; default: break; } } }
Copy
You should handle paymentQueue(_:updatedTransactions:)
and all states of these transactions.
However, in this article we will dive deep into .failed
states. As Apple mentions in their official documentation, you should get the error
field from this transaction and then check the list of SKErrorDomain errors.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .failed: handleFailedTransaction(transaction) case ... } } }
Copy
How to handle SKErrors:
func handleFailedTransaction(_ transaction: SKPaymentTransaction) { guard let error = transaction.error as? SKError else { return } switch error.code { case ...: } }
Copy
Let’s take a look at all the possible cases of StoreKit errors.
.unknownSKErrorUnknown code 0
This error SKErrordomain 0 return usually happens when an unknown or unexpected error has occurred. Usually, there is no need to take any action on the developers side, but here are a few recommendations to check:
Check the LocalizedDescription property of the error object.
If the error appeared during the testing, try logging out or try it with a new test user.
The problem might be on the user’s account side, so it is worth asking the users to check and see if anyone gets back to you with more information.
.SKErrorClientInvalid code 1
This error is displayed when someone without the required permissions tries to perform the action. No action is needed to be done on your side. However, it is possible to try and show an automated notification with a message related to the action such as: “due to …. you are not allowed to perform the action. Please, change your account or device”.
.SKErrorPaymentCancelled code 2
This error code indicates that the user canceled a payment request. No action is needed from your side, however, you could try to look at the user’s behavior to find the right trigger to encourage the user to complete the payment in the future – such as an offer or triggered email that talks about the value of your app. The best reaction to this error would be to catch the event and send the user an automated notification with a discount offer or something else of value to try and encourage them to continue using your app.
.SKErrorPaymentInvalid code 3
This error shows that the payment was not processed correctly due to an issue with the billing – Error code indicating that one of the payment parameters wasn’t recognized by the App Store, card expiration or not enough funds. Try to check whether the billing issue is related to card expiration and if so, the best idea is to catch this error, and send an automated notification or email with a reminder to your user.
.SKErrorPaymentNotAllowed code 4
An error code that indicates that the user is not allowed to authorize payments.
.SKErrorStoreProductNotAvailable code 5
This error might happen when your user tries to purchase a product that is unavailable in their region. Check SKStorefront.
.SKErrorCloudServicePermissionDenied code 6
Error code that indicates that the user has not allowed access to Cloud service information.
The error state is one of the five UI states. It is pretty important in regards to the user’s experience and satisfaction. If you don’t handle errors probably, they can quickly become the main reason why users lose control of the app, and as a consequence, lead to a reduction in retention or even deletion of the app.
Error handling is more important when you work with dependencies, especially when working with a library like StoreKit. In general, StoreKit is a black-box in which anything can happen. In this short article, we will explore how to handle failed transactions and give feedback to the user with appropriate error messages.
![](https://framerusercontent.com/images/Lm1h5AfgilFZog1v7Q411tGGbMs.jpg)
![Handling StoreKit Errors](https://framerusercontent.com/images/BF8PX4EhMo3uh1FNplZaE0hpo4.jpg)
NSError Overview
Variety of SKErrors
Errors associated with payments, store products, and cloud services occur under the domain model SKErrorDomain
and error codes SKError.Code
. The entire list of errors includes more than 15 options, you can find them here below and in the documentation. In addition to SKErrorDomain
errors, there may be network problems that occur in the NSURLErrorDomain
model. Let’s take a look at how to handle them all.
There are a lot of types of errors, so I‘d recommend dividing them into several groups:
external, which cannot be influenced in any way, for example, network problems
request errors such as incorrect product ID
user side errors such as canceling an operation or an unverified account
Handling StoreKit errors
For error handling, we need to implement two methods of the SKRequestDelegate
delegate.
optional func request(_ request: SKRequest, didFailWithError error: Error)
Copy
This is an optional method that receives errors from SKRequest
. It is important to keep in mind that when these errors occur, (void) requestDidFinish: (SKRequest *) request
will not be called.
The second point of failure is transaction processing, here errors are quite varied and can be associated with both when the user canceled the operation, and when rights have been restricted, such as parental controls. In order to be able to receive errors when working with transactions, it is necessary to process the the transaction status SKPaymentTransactionStateFailed
separately. To get these events you should have a skpaymenttransactionobserver
.
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing: break; case SKPaymentTransactionStatePurchased: // implement handling purchased state // [self handlePurchasedTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self handleFailedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: // implement handling restored state // [self handlePurchasedTransaction:transaction]; break; default: break; } } }
Copy
You should handle paymentQueue(_:updatedTransactions:)
and all states of these transactions.
However, in this article we will dive deep into .failed
states. As Apple mentions in their official documentation, you should get the error
field from this transaction and then check the list of SKErrorDomain errors.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .failed: handleFailedTransaction(transaction) case ... } } }
Copy
How to handle SKErrors:
func handleFailedTransaction(_ transaction: SKPaymentTransaction) { guard let error = transaction.error as? SKError else { return } switch error.code { case ...: } }
Copy
Let’s take a look at all the possible cases of StoreKit errors.
.unknownSKErrorUnknown code 0
This error SKErrordomain 0 return usually happens when an unknown or unexpected error has occurred. Usually, there is no need to take any action on the developers side, but here are a few recommendations to check:
Check the LocalizedDescription property of the error object.
If the error appeared during the testing, try logging out or try it with a new test user.
The problem might be on the user’s account side, so it is worth asking the users to check and see if anyone gets back to you with more information.
.SKErrorClientInvalid code 1
This error is displayed when someone without the required permissions tries to perform the action. No action is needed to be done on your side. However, it is possible to try and show an automated notification with a message related to the action such as: “due to …. you are not allowed to perform the action. Please, change your account or device”.
.SKErrorPaymentCancelled code 2
This error code indicates that the user canceled a payment request. No action is needed from your side, however, you could try to look at the user’s behavior to find the right trigger to encourage the user to complete the payment in the future – such as an offer or triggered email that talks about the value of your app. The best reaction to this error would be to catch the event and send the user an automated notification with a discount offer or something else of value to try and encourage them to continue using your app.
.SKErrorPaymentInvalid code 3
This error shows that the payment was not processed correctly due to an issue with the billing – Error code indicating that one of the payment parameters wasn’t recognized by the App Store, card expiration or not enough funds. Try to check whether the billing issue is related to card expiration and if so, the best idea is to catch this error, and send an automated notification or email with a reminder to your user.
.SKErrorPaymentNotAllowed code 4
An error code that indicates that the user is not allowed to authorize payments.
.SKErrorStoreProductNotAvailable code 5
This error might happen when your user tries to purchase a product that is unavailable in their region. Check SKStorefront.
.SKErrorCloudServicePermissionDenied code 6
Error code that indicates that the user has not allowed access to Cloud service information.
Start Now for Free
Or book a demo with our team to learn more about Qonversion
Start Now for Free
Or book a demo with our team to learn more about Qonversion
Start Now for Free
Or book a demo with our team to learn more about Qonversion
Read more
Read more
![Trash Panda App partners with Qonversion](https://framerusercontent.com/images/fIjbGE1BlBzu0OZK7vCihIvo.webp)
Trash Panda Maximizes App Revenue after Setting the Best Subscription Price with A/B Tests
Jul 8, 2024
Jul 8, 2024
![StyleDNA with Qonversion](https://framerusercontent.com/images/0CYKPTzYfOn6vdokDrwSoFR4c.webp)
How StyleDNA Saved 20% Development Time and Unlocked New Features
Jun 19, 2024
Jun 19, 2024
![WWDC 24 updates](https://framerusercontent.com/images/OFmq7UmE6o7QKdnnbRBNKtgo.png)
WWDC24 Updates for App Developers | What's new in Storekit 2 and App Store Server API?
Jun 17, 2024
Jun 17, 2024
![Parenting by Iben Sandahl uses Qonversion](https://framerusercontent.com/images/qJ2twjlmMxXqQ2MggeZqrVbB580.webp)
How A/B Testing with Qonversion Helped Iben Sandahl’s Parenting App Double Their Sales
Jun 13, 2024
Jun 13, 2024
![Trash Panda App partners with Qonversion](https://framerusercontent.com/images/fIjbGE1BlBzu0OZK7vCihIvo.webp)
Trash Panda Maximizes App Revenue after Setting the Best Subscription Price with A/B Tests
Jul 8, 2024
Jul 8, 2024
![StyleDNA with Qonversion](https://framerusercontent.com/images/0CYKPTzYfOn6vdokDrwSoFR4c.webp)
How StyleDNA Saved 20% Development Time and Unlocked New Features
Jun 19, 2024
Jun 19, 2024
![WWDC 24 updates](https://framerusercontent.com/images/OFmq7UmE6o7QKdnnbRBNKtgo.png)
WWDC24 Updates for App Developers | What's new in Storekit 2 and App Store Server API?
Jun 17, 2024
Jun 17, 2024