Streaming is available in most browsers,
and in the WWDC app.
Introducing StoreKit Testing in Xcode
Discover StoreKit Testing in Xcode — a local environment for testing your in-app purchases without needing to connect to App Store servers. We'll show you how to set up a test environment, create a StoreKit configuration file, and prepare to validate receipts locally. We'll also explain how to test a variety of in-app purchase scenarios and automate those tests with the StoreKitTest framework, and cover the latest improvements to testing in the sandbox environment.
♪ Hello, and welcome to WWDC. Dana DuBois: Hello, and welcome to Introducing StoreKit Testing in Xcode. My name is Dana, and I'm really excited to show you all the great new features we're introducing this year that'll make building and testing in-app purchases better than ever. But before we get into what's new, let's quickly review how developers work with StoreKit to add in-app purchases to their apps today. Currently, if I'm looking to update my app, either to begin selling content through StoreKit, or to add new content to sell, one of the very first steps I have to take is to sign in to App Store Connect. There, I need to register my app, define my in-app purchases, and create my Sandbox accounts for testing. Only after I've taken those steps can I go back to Xcode and start writing the code to integrate StoreKit within my app.
As I'm building up my app, I'll be working with the Sandbox environment to test my in-app purchases using one of the Sandbox accounts I set up in App Store Connect. There I won't be charged as purchases are made during my testing. Additionally, when I hand off my app to be beta tested. the Sandbox environment will be continued to be used by developers working with my app. Only after I verified my app works as expected, will I release my app to the app store, where the production environment is used to back all StoreKit calls. This has worked well in the past, but we think we can do better for you, our developers. I'm excited to announce this year, with Xcode 12, we're introducing a new development and testing suite dedicated to StoreKit and in-app purchases. This will allow you to start right in Xcode to build and test your app's in-app purchases entirely locally. This is all integrated right into Xcode, so it works seamlessly with your app's development workflow. Additionally, we're introducing a brand new StoreKit test framework that will unlock the full automation testing for your StoreKit integration. This changes the entire development lifecycle for StoreKit. Now instead of starting in App Store Connect, you can start right in Xcode. Build out your StoreKit integration at the same time you're building out the rest of your app.
Add in unit UI and other automation tests to make sure the quality of your app's StoreKit integration remains high, then sign in to App Store Connect and begin using the Sandbox environment. Working with Sandbox is a critical step you need to take to make sure your app is ready to be released to your users in the App Store. It's also the environment you need to use to test out any server-to-server functionality required for your app. Additionally, we're adding a number of great enhancements to the Sandbox environment this year that my colleague Chris will go into more detail later on in the session.
Let's dive in and take a closer look at StoreKit testing in Xcode 12. The app I'm building, Fruta, is all about fruit smoothies and the feature I'm working on right now is around selling recipes for those smoothies. StoreKit is the right technology to use as those recipes are digital content I want to offer within my app. I've already configured Fruta to fetch and render those store products for the recipes. If I navigate to Store.swift, where I keep most of the store content within my app, you can see I have a list of product identifiers for all the different recipes I want to sell. Further down within the fetchedProducts function, I have a SKProductsRequest call where I'm passing in all the product identifiers from those recipes. Fruta should be ready to go for fetching and rendering those products. So let's launch it in the simulator and see what happens.
As you can see, none of my recipes are showing. But this is expected, because I've not yet configured my app or in-app purchases within App Store Connect, and by default, StoreKit is using the Sandbox environment. So I need to enable local testing within Xcode. To do that, I'll first need to define my in-app products inside my project. First, I navigate up to the file menu, create a new file.
There I'll type in StoreKit and select the StoreKit configuration file template. And I'll just create a new configuration file right inside my project. And now I'll have a place to store the metadata for all my in-app products; the recipes that my app will offer. In the bottom left-hand corner of this editor, I have the option to add StoreKit items to this file. As I select that, I'm given a choice over which type of product to add. The first option is for a consumable, which is a type of product that you can buy over and over and over again, such as lives or gems within a game. The second is for a non-consumable, which are purchased once and never expire. Lastly we have auto-renewable subscriptions where users are charged periodically for access to services or content. I'm going to choose to create a non-consumable as that makes the most sense for purchasing digital recipes. And now I need to fill out the details for the recipe I want to sell. In this case, I'm gonna choose Berry Blue as it's my favorite smoothie. The product ID is a unique identifier for this specific product within my app and it needs to match the value that my app will pass in to the StoreKit APIs. To make sure I get it right, I'm just gonna copy and paste it from Store.swift.
Next I need to set a price. Unlike in App Store Connect, where I can choose from a price tier, here I'm given the option to enter in any decimal value. This is because for StoreKit testing in Xcode the price is simply used for setting what value is returned in the SKProduct that is handed back to my app. So here I have direct control over the value and I can easily change it to make sure my app can handle any possible value returned. For now, I'm going to leave it as 99 cents. Additionally, I have the option to enable Family Sharing. This is a brand new feature we're releasing this year. For the purposes of StoreKit testing in Xcode, enabling Family Sharing will update the IsFamilySharable flag on the SKProduct. I'm going to leave that unset. Lastly, I need to give this product a localized name within my app.
I can also set a description, but for now, I'm going to leave that blank. Now that I've set up my StoreKit configuration, I need to tell Xcode to use it instead of Sandbox when launching my app. Doing that's as easy as opening up the scheme editor, and under Run Options, selecting that configuration I just created. Now when I relaunch my app, StoreKit is using the local test environment. And the SKProduct returns the metadata I've configured. Earlier I skipped setting a description for Berry Blue. But I want a description to show up and Fruta is already set up to take the localized description from the SKProduct and display it. StoreKit testing in Xcode is entirely interactive, meaning I can update the metadata from my app's products without recompiling or even relaunching. I just need to update the StoreKit configuration file to have the description I want.
And have my app re-execute the SK product requests to pull down the updated metadata.
My project already contained a StoreKit configuration that has all the recipes I will be selling, so I'm going to quickly switch over to that file and relaunch my app.
My app is already configured to take the SKProduct that we fetched and use it to create an SK payment and add it to the payment queue. To exercise that code, I just need to select one of the buy buttons.
Right here in the simulator, I get a payment sheet a lot like what my users will see. The big difference here is that I didn't need to sign in and there is no need to authenticate since this is just for testing purposes and I won't be charged. I simply select confirm and the payment goes through. Just like Sandbox or production, my app's payment transaction observer is updated as the transaction goes from purchasing to purchased. I'm even given a receipt that my app can verify. Berry Blue is now unlocked and I can navigate in and see the recipe. Now let's say you had an issue where I needed to purchase Berry Blue all over again, possibly to fix a bug or to change the experience around purchasing. Xcode makes that really easy with the new StoreKit Transaction Manager.
I see all the purchases I made in the local test environment and I have full control over them. I'm able to delete this transaction, and reset state as if the purchase never happened.
I can now buy Berry Blue again as if I'd never purchased it before.
In addition to being able to delete previous purchases, I have the ability to simulate a refund. In the case of a refund, the transaction remains in the transaction manager but is marked as refunded.
The transaction also remains in the app's receipt, but is updated to contain a cancellation date for when the refund occurred. Again, my app is informed about the refund and is able to respond immediately. If you want to make sure your app has a great experience for kids who need to get permission before making purchases, StoreKit testing in Xcode has full support for Ask to Buy. It's as easy as going back to your StoreKit configuration file and enabling Ask to Buy under the editor menu.
Now when I go to purchase a recipe within Fruta, I see the same Ask Permission dialogue that will be presented to any user who has Ask to Buy turned on for their account.
When I select "Ask," the transaction appears as pending approval within the transaction manager. And in code, my app's SKPayment transaction observer is informed that the transaction is now in a deferred state.
I can now approve or decline it. Once I approve, the previously deferred transaction is updated to a purchase state and my app updates right away to show the unlocked recipe. I also have the option to enable interrupted purchases. This will allow me to simulate the case where a user needs to take some action on their account before being able to complete a purchase. For example they may need to update details related to their account's billing information. In this case, the initial purchase will come back as failed. But once the user has taken action to resolve whatever issue caused the interruption, a new transaction for the same product will be added to the payment queue.
I've been considering changing the business model of my application to be subscription based. For one monthly price, users will have access to all the smoothie recipes instead of needing to buy them one at a time. StoreKit Testing in Xcode makes it really easy to build auto-renewable subscriptions with StoreKit. I've already created a second StoreKit configuration file that contains two different subscriptions I want to offer in my app. First, Recipes Plus, which is configured to renew at a monthly price of $2.99.
To make it even more compelling, I've set up an introductory offer of 99 cents for the first month. I've also created a Recipes Pro option, which not only unlocks all the recipes in my app, but gives subscribers access to advanced nutrition facts for each smoothie. The two subscriptions are contained in the same subscription group, enabling my app to offer upgrade options between Recipes Plus and Recipes Pro. I've already built Fruta to work with these subscriptions, so to try out that business model, I just need to switch StoreKit configurations and relaunch my app.
Here my app is displaying details about both the subscription offers, including the introductory offer on Recipes Plus. I want to make sure my app correctly handles when my subscription auto-renews, but I don't want to wait a full month to test it out. With StoreKit testing in Xcode, I have the power to speed up time. In the editor menu of my subscription configuration, I select Time Rate, and I can set one second to be one day. This will change it so we only have to wait seconds for that renewal to occur instead of an entire month.
Now that I've purchased that subscription, all the recipes are unlocked. And I won't have to wait very long for the Recipe auto-renewing subscription to automatically renew.
Earlier as I was working with the transaction manager to delete and refund purchases, my app immediately updated to revoke access to those recipes. This was made possible in iOS14 thanks to a new API we added on SKPayment transaction Observer: didRevokeEntitlementsForProduct Identifiers. To learn more about this API and all of the other new features we've added to StoreKit this year, I highly suggest you check out What's New with In-App Purchases. To enable you to authenticate the purchased content in your app, the App Store provides a digitally signed receipt. The receipt is the trusted record of purchases made by the user in your app. It's stored on the device and updated automatically by the system. It is signed so you know that it came from the App Store and was meant for your app on that device. If you want to learn more on how to verify the receipt, I recommend checking out Best Practices and What's New with In-App Purchases from 2018. But I do want to highlight some key differences you need to keep in mind when working with the receipts generated by the local Xcode StoreKit test environment. First, they are signed with a different private key than what is used for the receipts generated in the Sandbox or production environments. That means you need to use a different certificate when validating. We provide a convenient way to export that StoreKit Test Certificate into your project via the editor menu of your StoreKit configuration file. Lastly, the StoreKit Test Certificate is not part of a certificate chain. To accommodate for those differences, you should make use of the debug macro within your client-side validation code. Here you can see where my code is choosing between the StoreKit test certificate and the Apple, Inc. Root Certificate, based on that macro. Additionally, if I use open SSL for my client-side receipt validation, I would pass in the PKCS7 no chain argument, but only when building for debug. StoreKit Testing in Xcode 12 work great on all of the OS betas that we just released, both when building and running in the simulator as well as on real devices. I walked you through setting up your project StoreKit configuration, enabling the local test environment, using the transaction manager to delete and refund previous purchases, and making sure your app has a great experience for deferred or interrupted purchases, and purchases that renew automatically. But as critical is it is for you to be able to build and test your store kit integration manually, it's just as important for you to build automation that continuously tests your in-app purchases, which is why we're introducing a brand new StoreKitTest Framework that enables you in code to control the full local test environment. All the controls that you had manually are exposed inside your tests. StoreKitTest works with XCTest for extending unit and UI test coverage to your in-app purchases. Additionally, with StoreKitTest you have the ability to disable all the sheets and dialogs that would normally appear. So your tests can run to completion without waiting for user interaction. Lastly, you have the ability to trigger the renewal of the subscription right away so your test can validate if your app's subscription features continue to work across renewals.
StoreKitTest can be used to cover a wide range of cases including your app's handling of both successful and failed purchases, interrupted and deferred purchases, transactions that initiate outside the running of your app, as well as all sorts of cases related to subscriptions. Let's go back in Xcode and run through an example. In Fruta, I've already built out a series of unit tests covering cases related to in-app purchases. The first one is a basic test case that ensures my App correctly unlocks the Berry Banana smoothie recipe when successfully purchased by a user. The very first thing it does is creates a new Store KitTest session initialized with the non-consumable StoreKit configuration file. It's important to remember to include the configuration file in the test targets so that's referenceable by SK Test session. Next I'm disabling all dialogs on my session so that my test can run to completion without user interaction. After that, I'm clearing all previous transactions so I know that I'm starting from a clear state. Finally, my test buys the recipe and confirms that it's available.
Test succeeded, so I know I'm good to go. At this point, I can begin the process of getting my app in in-app purchases ready to be available in the App Store. The critical next step is to sign in to App Store Connect, set up everything the App Store needs to support my in-app purchases and test that configuration using the Sandbox environment. To tell you all about all the great enhancements and features we're bringing to Sandbox, I'm gonna hand it off to Chris. Chris Markiewicz: Thanks, Dana. Hi, I'm Chris, a program manager for the App Store. We saw how StoreKit Testing in Xcode enables you to perform early testing and debugging of in-app purchases in a local test environment. There are some key differences between StoreKit Testing in Xcode and testing in the Sandbox environment. First, you set up in-app purchases in App Store Connect. This is the same product information your app uses when it's available on the App Store, and you'll create a Sandbox Apple ID to sign into your test device. In Sandbox, app receipts are signed by the App Store. Sandbox also supports server-side receipt validation and App Store server notifications. In iOS 12, we added a Sandbox account section in app store settings for signing into a Sandbox account, an account which you set up in App Store Connect. In iOS14, it's easier to test your in-app purchases and subscriptions in Sandbox. A new Manage option leads you into the new Sandbox manage subscriptions page where you can view and test your subscriptions in Sandbox. Tapping into our Fruta app, we see all subscriptions within the subscription group. You may have used this page on production as a subscriber for selecting a different subscription to upgrade, downgrade, or cancel. Now this page is available to you as a developer to test your subscription tiers in the Sandbox environment. We added the ability to reset introductory offer eligibility for a subscription. With this, you can use the same Sandbox test account to test free trials or introductory offers. You no longer need to set up a new Sandbox Apple ID each time you want to retest an introductory offer.
Here's an example of subscription lifecycle. With this new functionality in Sandbox, you can test common subscription changes which happen outside of your app, including customer actions such as a downgrade, an upgrade, or a cancellation. You can test server logic for subscriber status change notification types such as DID_CHANGE_RENEWAL_STATUS and DID_RENEW, a new notification type coming later this year in production which you'll also receive in Sandbox. To learn more about server notification types, please refer to the session What's New with In-App Purchase. Coming soon to App Store Connect, Sandbox accounts will be able to test interrupted purchases on a device. To use this, select a row for a Sandbox Apple ID. Selecting Cindy's account, check the box to enable interrupted purchases, then Save. Now Cindy can test interrupted purchases in Sandbox on an iOS device. A scenario would be needing to agree to updated terms and conditions before completing the purchase. Purchases on a device will continue to be interrupted for an individual Sandbox account until you return to App Store Connect and disable interrupted purchases for each tester. Let's review the new iOS 14 subscription and in-app purchase testing functionality in Sandbox. On a device you can now view and manage subscriptions. You can also test upgrades, downgrades, cancellations, and reset introductory offer eligibility. Soon you'll be able to test on a device how your app responds to an interrupted purchase. Lastly, in App Store Connect, users with the developer role will be able to create and manage Sandbox tester accounts. Testing in the Sandbox environment is a great way to improve your app's handling of in-app purchases before releasing into production. For a recap, here's Dana. Thanks, Chris. There's a clear path for building and releasing high-quality apps that use StoreKit. Start in Xcode 12 and use the interactive local test environment to build out your StoreKit integration.
Use the StoreKitTest framework to build tests and automation to ensure the quality of your app's subscription and in-app purchase functionality. Sign into App Store Connect, set up everything the App Store needs to support your in-app purchases, and test that configuration using the Sandbox environment. Additionally use Sandbox to test all server-to-server functionality required for your app. Take advantage of TestFlight to get beta testing coverage of both your app and its subscription or in-app purchase functionality and then make your app live on the App Store. Thanks again, and we look forward to seeing the great apps you're going to release.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.