What’s new in StoreKit testing (WWDC22 updates)
Tags: storekit, ios, wwdc22, testing
Published: 2022-06-13 07:12:57

New day, new [WWDC22 session](https://developer.apple.com/videos/play/wwdc2022/10040/), new overview! Today we’ll walk you through the updates in StoreKit testing. 

Before we move on, I’d like to emphasize that previous significant updates for StoreKit testing were introduced during WWDC20, which we’ve also [covered here](https://qonversion.io/blog/storekit-testing-in-xcode-12-and-ios-14/).

Now, it’s time to move on to the brand new updates! 

What we will cover today:

- StoreKit updates in Xcode 14
- Advanced subscription cases
- Sandbox updates

## StoreKit updates in Xcode 14

During WWDC20, the StoreKit.configuration file was announced. This unlocked the capability to test in-app purchases without needing to set up the Products in App Store Connect. 

This allowed you to set up in-app products directly in StoreKit.configuration file, and this local file would be the only place that contains these products and purchases.

The latest WWDC22 updates require you to create the app and products in App Store Connect. You still have an option to test your in-app purchases through StoreKit.configuration directly in Xcode without having an app in App Store Connect. The only limitation of this commonly used approach is that you will not have the chance to use a part of a new features. 

You may wonder: what are the features of this and are they worth all this hassle with app creation in App Store? My answer is: absolutely! 

By having an app with the products in App Store Connect, you can create a new configuration file in Xcode that will synchronise these products with those that were created in App Store Connect. Any changes that you make within App Store Connect – whether changing the title of the product or doing something else – can be synced with this Xcode Configuration file.

Additionally, you can convert this new file into the local version that won’t be synced with App Store Connect, but will give you an option to edit your products directly in Xcode. Be accurate and careful here, because this is a one-way operation. You will not have a chance to convert the local file back to the synced version. So, in order to proceed with further syncs you’ll need to create a new file. A great option is to have several StoreKit.configuration files; for instance, you could have both synchronized and local ones. 

To create the new StoreKit.configuration file you should go to Xcode Menu => File => New (or lock Command + N). Then, type storekit in the search, select StoreKit.configuration file, and push the “Next” button.

Here’s a screenshot to clarify this process:

![Storekit.configuration file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbthc2o91lydyoex5h57.png)

In Xcode 14 while creating this file you’ll see the checkbox that enables the syncing of in-app products with App Store Connect. To turn this option on, you need to choose the Team and App.

![Storekit.configuration file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvqgdid4l7om1b9mclqp.png)
 
It will be pretty clear to you that to create the local file you should just fill the name of the file and leave the checkbox unchecked. 

Once you create a configuration file, all the information about your Products begins to sync with App Store Connect. In the lower right corner, you’ll notice the indicator showing you that data downloading is in progress. Even though this job is in progress, you can still continue to work in Xcode. 

Those who used to work with the local version of StoreKit.configuration will notice that the newly created file differs from the files that they used to see. The reason for this is that this local version of StoreKit.configuration is read-only. All the changes in products should be made with App Store Connect. 

As we highlighted before, all the changes in products that were made in App Store Connect can be synced in StoreKit.configuration file just with one button below. Once the sync is complete, you can see the changes reflected in Xcode.

![Sync](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8hxyj7gwm4puxsld2kk9.png)

As I mentioned, this is a read-only file. 

You still can easily copy the Product from this file and move it to the local StoreKit.configuration file in Xcode. To do so, right click to the Product that you’d like to copy and click “Copy”. Then choose the local file and open Xcode Menu => File => Paste (or hold command + V).

Alternatively, you can convert this synced file to the local one. You can’t undo this operation, but if necessary you can create a new synced StoreKit.configuration file. Xcode Menu => Editor => Convert to Local StoreKit Configuration.

Let’s move on to the testing.
To start this process you should choose the StoreKit.configuration file in Xcode that should be used for building the app. This is a commonly used method.

Then open Scheme Editor: 
![Scheme editor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vuqmz4xdjhbnlxm2oa1n.png)
 
Choose the “Run” action, then “Options”, and then choose the right file in the StoreKit Configuration:

![StoreKit Configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/thvaqe2qtg8hl1r8lowj.png)
 
Congrats on the successful set up of your testing environment! Now you can configure your app.

One more great update for those who use SwiftUI: starting with Xcode 14, all the Products from StoreKit.configuration files will be uploaded to SwiftUI previews. 

Let’s move on to the testing. As you might remember, WWDC20 introduced the Transaction Manager. You can open it by clicking on the “Purchases” icon in the debug bar. 

Xcode 14 Transaction manager includes Transaction Inspector (on the right side) that displays under-the-hood details about a transaction. This is a super convenient way to check subscription statuses. For instance, here you could see the information about expired subscriptions or subscription renewals.

![Xcode 14 Transaction manager](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp3h5lh1q21dmmny0eu3.png)

Also, by clicking these buttons you can move to the specific Product in StoreKit.configuration file in Xcode.

![Xcode 14 Transaction manager](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrxt1gemksjyiry6f7cz.png)

Moreover, at the bottom part of the inspector, there is a field to filter transactions. For instance, you can filter by the day of creation of the transaction or by Product ID. This significantly simplifies the way you navigate all your transactions (it is quite a daunting task, as you have all subscription renewals that may confuse your detection of which transaction entitles the feature).

## Advanced subscription cases

The new version of Xcode allowed the chance to test things in a more convenient way, and to test something that was previously impossible to test. This is really exciting! 

What was added: 

- Refund requests 
- Offer codes
- Price increases 
- Billing retry and a grace period 

### Refund requests

As you might guess, the option to cancel the subscription directly in the app was released. It means that you can test this functionality for your app. 

To show the corresponding screen you need to call just one function: [refundRequestSheet(for:isPresented:onDismiss:)](https://developer.apple.com/documentation/swiftui/view/refundrequestsheet(for:ispresented:ondismiss:))

The refund request sheet will appear above the view. It will include the list of refund reasons, and this list corresponds to [revocationReason](https://developer.apple.com/documentation/appstoreserverapi/revocationreason) property in the App Store Server API.

It looks like this: 
![Request Refund](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w9uu1m75vosgktcq8kqb.png)
 
If we are speaking about the Product in App Store, you should remember that refunds can take some time to process; however, when testing with Xcode or Sandbox, a refund request will immediately refund the transaction. You could see this refunded transaction in the Transaction Inspector. You’ll see both the refund reason and the refund date there. 

Moreover, you can test the refund directly in the Transaction Manager by clicking the “Refund purchases”.

![Refund Purchases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zagrxrlzaz8n7sfac30s.png) 

We’ve described how to test refunds, but how does it look from the code perspective?

The [Transaction](https://developer.apple.com/documentation/storekit/transaction) object that is received from StoreKit consists of [revocationDate](https://developer.apple.com/documentation/storekit/transaction/3803221-revocationdate) and [revocationReason](https://developer.apple.com/documentation/storekit/transaction/3803222-revocationreason) properties. This will help you detect the refunded transactions. 

![Refunds emit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/du62imvcav59vxrse229.png)
 
For iOS and iPadOS the testing through Xcode iIs available starting version 15.2, through Sandbox – starting 15.0. MasOS in Xcode – 12.1, Sandbox – 12.0. 

![Refund request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vk3pufe2upf9pwkt6air.png)

### Offer codes 

In the following example, let’s test offer codes redeem flow with the local configuration file. If you already have offer codes in App Store Connect, and you use synced StoreKit.configuration file, you’ll see these codes automatically in Xcode. 

For our case: go to the local file, click the “plus” in “Offer Codes”, configure Offer Code, and click Save.

![Offer Codes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7by5hty5ymsyia3o0zlc.png)

Once configured, it should be tested. The code implementation is simple in the case of refund requests. You just need to call the function: [offerCodeRedemption(isPresented:onCompletion:)](https://developer.apple.com/documentation/swiftui/view/offercoderedemption(ispresented:oncompletion:))

Now you should see the redeem offer code sheet above your app. Only the users of your production app should enter offer code in this field. While testing with Xcode, you won’t need to enter offer code in this info, but rather only to choose the offer code from the list (as all this data is kept in StoreKit.configuration file).
 
![Redeem code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oar01x3sa7173px1wsaq.png)
 
It’s incredibly easy: choose the offer code, confirm the operation – and profit!

Once the offer code is successfully processed, you’ll see this transaction in Transaction Manager; all the details will be available in the new Transaction Inspector. 

Let me skip the part where I explain the logic of managing this transaction, as it is quite straightforward. The only difference from managing the ordinary transaction is that you should check a few additional properties. 

While managing [transactions](https://developer.apple.com/documentation/storekit/transaction) you can check the [offerType](https://developer.apple.com/documentation/storekit/transaction/3822313-offertype) property to see if there is an offer applied to the current transaction. While handling [renewalInfo](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo), use its property [offerType](offerType) to see what kind of offer will be present in the next renewal.

Offer codes are an incredibly valuable tool for working with users in your app. There are so many things you can achieve, from user acquisition to winning churning customers and support/loyalty programs. So it’s incredibly valuable that we now have a ability to test it.

Just to remind you, this option didn’t previously exist, even in the Sandbox!

Everything described above is available starting Xcode 13.3 and higher and iOS/iPadOS 15.4 and higher. 

### Price increases

As you might know from our [previous overview](https://qonversion.io/blog/whats-new-with-in-app-purchases-wwdc-2022/) of WWDC22, StoreKit Messages are aimed to be used to display messages from the App Store API to users. One example of such messages is a change in the app’s price. Let’s take a closer look at how we can test this scenario. 

First, change the price of the Product in the StoreKit.configuration file in Xcode. Alternatively, you can choose the needed transaction in the Transaction manager and click the “Request Price Increase Consent”.

![Price increases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/koqla7llt95sw06i5crd.png)

You can skip the flow of how to display the StoreKit Messages, but in this case the message will appear automatically over your app. If you’d like to avoid interrupting your user’s flow and do not want to affect user experience, you should implement the StoreKit Messages sequence the same way that you handle the transactions in StoreKit. Find these details [here](https://developer.apple.com/documentation/storekit/message/3954510-messages).

What is important is that this price-changing screen might be shown to your users several times until they consent to the new terms. In Xcode, you can manually call this screen only the needed number of times (read above). It is important to remember that the users might consent outside of the app – for instance, via email. 

In Xcode, you can test this consent screen display scenario just by clicking the corresponding buttons in Transaction Manager.
 
![Transaction manager](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xpuvia7vee4vx9lymt5t.png)

Let’s take a look at the code side. As you work with the purchases in your app, you probably are already implemented [Statuses.updates sequence](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/status/3851115-updates). The only thing that you should do now is to check [renewalInfo](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo)  => [priceIncreaseStatus](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo/3822292-priceincreasestatus) property to receive the needed state. Check the [expirationReason](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo/3749499-expirationreason) property to detect if the customer canceled the subscription after the price changed. 

![Price increase changes emit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s0vl5ztc6em4bxvu9w24.png)
 
All this is unlocked by starting the following versions of OS: 

![Price increase testing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ciqlyp7svpyl4ft482z.png)

Please consider that StoreKit Message testing on price changing is available just for iOS/iPadOS 15.4 version and higher.

### Billing retry and grace period

Billing retry is where an error occurred while trying to renew a subscription, such as if a credit card is expired. 

![Billing retry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yvyctok13okvyqxruibo.png)
 
In this case, App Store will attempt to fix the billing issue to renew the subscription.

![Billing retry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmm6o5fh431hcpzs9oxd.png)
 
You can enable a grace period that gives your customer the ability to use your subscription for a limited period of time at the beginning of the billing retry state. In other words, this is a period for the user to solve billing retry issues, pay for the subscription, and continue to experience the app.

![Billing retry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zz07cddi91t4m3hjjwn6.png)
 
Let’s take a look at how to test this case in Xcode. 

Choose your StoreKit.configuration file, then Editor Menu => Enable Billing Retry on Renewal. If you’d like to test the grace period, you can refer to the next point in the same menu.
 
![Renewal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2qqwvpivqbsqhydrtzt3.png)
 
For further convenience, you can speed up the subscription renewal rate from the same menu.

The next steps are straightforward. Let’s subscribe to the app then wait for the renewal period time (that you choose in the Subscription Renewal Rate menu). At the moment of renewal, you’ll see the new transaction with Billing Retry in the Transaction Manager. The Transaction Inspector will show that the transaction is in the grace period. Once the grace period expires, the transaction appears in the standard billing retry state. You can resolve the billing issue by clicking the corresponding button in Transaction Manager. 

![Transaction manager](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jpw0l9i4bdslym8gqlj.png)
 
Handling billing retries and grace periods is key to retaining subscribers by reducing involuntary churn.

Now it’s time to explore how you can handle this in the code.

As the billing retry and grace period states change, the [Status.updates sequence](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/status/3851115-updates) will emit a new value.

All that you should do is check, using the [renewalInfo.GracePeriodExpirationDate](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo/3749500-graceperiodexpirationdate) property, whether your user is in the grace period. If so, you should grant him access as if the subscription were active. To detect whether the user is in a billing retry state, check the [renewalInfo.IsInBillingRetry](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/renewalinfo/3749502-isinbillingretry) property.

![Billing retry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3m9pzpj8nmsbx9e6w8e.png)
  
Both of these states can be checked with [state](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/status/3749528-state) property in  [Product.SubscriptionInfo.Status](https://developer.apple.com/documentation/storekit/product/subscriptioninfo/status) you’d like, you can redirect your user to the App Store to fix the billing issues.

![Billing retry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al6e2f8jky5e5wo8ns0n.png)
 
If you are using any [currentEntitlement API](https://developer.apple.com/documentation/storekit/transaction/3851204-currententitlements), you’ll receive the transactions for expired subscriptions while they’re in the grace period.

In which versions is this feature available? Check the following: 

![Billing retry testing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpbcbh2dlu4bjlkjg10q.png)
 
So these are the exciting updates with Xcode testing! These will definitely improve the developers’ experience while working with in-app purchases. 

## Sandbox updates 

Last but not least – the updates related to Sandbox.

What we’ll explore: 

- Sandbox Apple ID creation
- App Store Connect API
- Billing failure simulation

### Sandbox Apple ID creation

To unlock the ability to test in Sandbox, you have to create a testing user.

There are several pieces of good news. 

- The number of fields that you should fill while creating the user significantly decreased. 
- You can use “+” symbol in email to prevent you from creating a new email for every user. For instance, “your_super_test+sandbox@iclod.com”
- Added in-line suggestions for passwords

![New tester](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00hc2jebbaqxkkw4wt11.png)

### App Store Connect API

For the last few years, Apple engineers have added a lot of features to Sandbox per developers’ requests. The changing of the Sandbox account region and clearing purchase history is one of these. 

Most of these features are available in App Store Connect or directly on the device on the Sandbox Manage Subscriptions page.

Later this year, the following Sandbox features will be added to App Store Connect API:

- Query for Sandbox Apple IDs
- Clear in-app purchase history
- Set interrupted purchase state

These features will improve the speed of subscription testing with Sandbox accounts and add possibility of automated testing in the basic scenarios.

### Billing failure simulation

Since launching in 2019, Billing Grace Period allowed developers to recover 300 million days of paid service to their customers. Soon, there will be the capability to use a new Sandbox Account Settings page to: 

- enable billing failure simulation for your account
- test foreground and background subscription failures
- verify subscription status with [verifyReceipt](https://developer.apple.com/documentation/appstorereceipts/verifyreceipt), [App Store Server API](https://developer.apple.com/documentation/appstoreserverapi) and [App Store Server Notifications v2 in Sandbox](https://developer.apple.com/documentation/appstoreservernotifications/app_store_server_notifications_v2).

## Conclusion

With these testing scenarios, Apple [explores](https://developer.apple.com/videos/play/wwdc2022/10039/) the statuses that will affect App Store Notifications V2. This is a big topic to cover, so stay tuned for other articles! 

We know a thing or two about in-app purchases as [Qonversion](https://qonversion.io/) provides a complete cross-platform infrastructure that allows you to create and restore purchases, validate receipts, and provide your app with an accurate subscription status without the need to build your server. So, If you’d like to learn more about it or have any questions, feel free to ping me in the comments. 

If you'd like to learn more on [how to handle StoreKit errors](https://qonversion.io/blog/handling-storekit-errors/), please follow our guide. Also, recently I've created an article on how to solve [SKErrorDomain 0](https://qonversion.io/blog/skerrordomain-code-0/), feel free to check!