The StoreKit API allows apps to request payment for additional functionality or content. Hear about the latest changes to StoreKit including Swift 3.0 APIs, updates to Subscriptions and using StoreKit with iMessage apps. Learn best practices for receipt management and Sandbox testing.
So my name is Dana DuBois. I'm an engineering manager on the team that writes the frameworks that help power the App Store on iOS and tvOS. And today we're going to talk about one of those particular frameworks, and that's StoreKit.
Many of you know, StoreKit's been around for a long, long time. It was introduced in iOS 3 for the iPhone.
Since then it's been used by thousands and thousands of apps to power all sorts of business models.
Everything from newspapers and magazines, games, even dating apps are using StoreKit. It's really widely used.
And we've talked about StoreKit a lot at WWDC, but this year we're going to do something a little new and we're going to talk about it using Swift. So that's really exciting. We've got some new APIs.
There's a couple other things that are new, so Swift API, so that's really great.
Last week, many of you probably also heard that we have a lot of big announcements around subscriptions. This is something we're really excited about as well.
I think a lot of developers are going to end up taking advantage of these new enhancements with subscriptions. So I'm going to give you a quick overview on them.
First up, categories.
We're really you know excited to announce that category will no longer be a factor if you're choosing to use auto renewing subscriptions in your applications.
There are still going to be criteria behind how you can use auto renewing subscriptions, but category will no longer be a factor.
Second, everyone's got to be excited about this, we're giving more proceeds to developers.
If you keep your subscribers for more than one year, that's really great. And I think that's going to be a big deal.
Pricing. A lot of control now we're going to be giving to developers through iTunes connect to set the pricing for their subscriptions based on territory to territory, region to region.
If there's a business reason you might need to adjust the price in Europe compared to the US, there's a lot of control that you now have over that. And then along with pricing, we're giving you the ability to save the price, to preserve the price for users who may have already been using your subscription while adjusting it for new users. So, if you have early adopters that you want to reward and you want to keep them using your subscription, but you want to adjust the price for newer users, you have that power now.
And then upgrades and downgrades, this is us giving the user a lot more power to control exactly what level of subscription they might want within your service. So if they want to go up to the platinum level, or go back down to the basic level, they can do that right in the Manage Subscription UI right on the device.
So that's a quick overview of what's new in subscriptions.
There's a lot more to it. I think if you're going to have subscriptions in your application, you really should go to Introducing Expanded Subscriptions in iTunes Connect, that's at Pacific Heights today at 4.
Really, they're going to get a lot more in-depth on how to set this up, how to manage this in your application and I really highly recommend you go to that.
So that's subscriptions. There's one other new thing that I'm really excited to announce, and that's iMessage apps.
We announced that yesterday at the keynote.
We're bringing apps, a whole new class of apps right into the iMessage app, iMessage.
You'll be able to build custom extensions, and we are going to allow in-app purchases right in those extensions, right in iMessage aps using the exact same StoreKit APIs you would use in a normal application that you have in the store today.
So if you have additional content that you want to market or provide right inside the iMessage experience, that's now supported. And this will all be available in iOS 10, so that's something we're really excited about and I think developers are going to take a lot of advantage of that.
So as I said before, StoreKit's been around for a while. People have been using in-app purchases for a long, long time, but maybe some of you are new to it. So I'd like to just give a quick overview of what exactly in-app purchases are.
In-app purchases are digital content or service that can be bought right inside your application.
However, to be clear, it's not for physical goods. There's other ways of doing that. This is really just about digital content or services. Now when we're talking about digital contents or services, there's a bunch of different types. When you're configuring this in iTunes connect, you have a lot of options and you need to make sure that you choose the right type that fits your content.
So let's take a look at what those are.
Everybody's played a game and they have coins in that game, currency, or gas in your racing car, or something that they user's going to buy, and buy again, and buy again, and use up over time. That's what a consumable purchase is.
A non-consumable product is something that sticks around, it will stick around for as long as the user wants to use it. They'll be able to restore it. They'll be able to move it from device to device.
So that's a little different. And again in a game you might have a sword, or you know some sort of weapon, or you know a racing car, or a level, or if you have a utility app, you might have a basic edition of your app and you might want to offer a pro-edition of your app.
That's kind of a non-consumable product.
We also have two types of subscriptions when we're talking about subscriptions. There's non-renewing subscriptions, as the name suggests these are subscriptions that don't automatically renew, it's up to you and your application, and your back end to manage how to renew those subscriptions. But then we also have auto renewing subscriptions. And these are subscriptions that Apple will bill to the customer on a periodic basis, based on what you configure. So if it's a monthly subscription, we'll bill the user every month, as long as they continue to opt into that subscription.
So these are the types, let's get into the heart of adding in-app purchases to your application. So, what I'm going to do is I'm going to do a quick overview of all the various steps, and then we're going to dive into each one and we're going to talk about how to do this in Swift. And you know exactly what you might need to worry about as far as pitfalls, or any concerns you might have along the way. There's a lot of things that can get kind of tricky sometimes when you're developing against StoreKit. A couple of things to keep in mind. So first off you're going to want to figure out exactly what it is that you're going to sell to the consumer, the user of your app. So this is done just by determining the identifiers, those in-app identifiers of what you're marketing to the user right then and there.
Once you have the identifiers, you go off to the App Store and you fetch the information, localized product information that's related to those identifiers. And that's key. This is localized information. This is a global marketplace, you want to make sure that you're displaying the information to your users in a localized way.
And now once you have that information you're going to show off the products. You're going to market them. You're going to sell them. These are your products, this is your application, you're going to build the best UI you can to show them off.
The users then are going to be enticed to make that purchase. They're going to be you know, sold, and they're going to select the product right then and there.
But it's up to you to then create a payment for that product and add it to the Payment Queue. And we'll be talking a lot more about the Payment Queue as we go on.
As the payment is going through the Payment Queue your app will be notified about that transaction and you're going to have to make some response to it, and that's how you process these transactions.
Finally, when the payment is completed, you're going to make that product available.
Give the user what they paid for. And then finally tell the Payment Queue you're done with the transaction, everything's completed, the product's available, so that's finishing the transaction.
So those are all the different steps you need to take. Real quick. We're going to dive into each of those as we go through, but first special note about the Payment Queue.
The Payment Queue is the center of your in-app purchase implementation.
It's the source of truth for state about purchases and payments as they're going through all the way from when the user selected that product to when it's paid for.
And it's important to keep in mind that you should rely on the queue and only the queue to know about transactions that are progress, payment updates, and if you're using hosted downloads, the Payment Queue will tell you all about those downloads.
And then another thing to keep in mind is that if the Payment Queue tells you about a payment, it's a valid and real payment.
In fact, there are cases we've seen where developers might have their own side cache, where they've noticed that a user has clicked on a product and they're managing state about that payment.
And then their app will crash, or something will happen along the way, the user will close the application before finishing the payment, and they'll come back into the game, or whatever it is that they were purchasing then there.
And they don't necessarily respect the response coming from the Payment Queue because they weren't listening to those payment transactions because their own queue no longer had them in state.
So you should really just rely on the Payment Queue to tell what's going on with those payments as they're happening. And in fact it's very important and we're going to get into some code here, to listen and observe the Payment Queue right when your app starts up. This can be done in the example we have here, we have didFinishLaunchingWithOptions is the first thing that happens with your application when it starts up and we create a payment transaction observer, in this case it's the app delegate itself. And we set it right on the Payment Queue, we add it to the Payment Queue.
And then if there's anything in the Payment Queue that may have been there before our app launched, we'll get notified about it right away.
Another kind of case that comes up is there are applications that have in-app purchases where they have redeemed codes. So the user might have actually gone into the App Store and redeemed that in-app purchase completely outside of your app. The user then is exciting about consuming that in-app purchase, they launch your application, and if they're not listening to the Payment Queue that product won't become available.
You got to do it right when the app starts up.
So that's a quick note about the Payment Queue.
Let's get back into the process and understand exactly how to add this to your application.
So the first thing you want to do is load the identifiers for your in-app purchases.
These are the same identifiers that you set up in iTunes Connect. You define them, you name them.
And you just need to get a list of them.
There's a couple different ways of doing it, if your app is very simple, you might just bake the identifiers right into your application. If you have an application that has a basic version and a pro version, you might just have one consumable and you just baked it right into your application.
However, we've seen more and more applications out there where they're interested in going out and fetching those in-app identifiers from a host that they provide so that over time maybe they can change what they're marketing to the users, or exactly what appears where.
That's okay to do. Completely valid to do. The thing we want to call out is if you're doing that make sure your host is scalable and reliable. This is the beginning of your in-app purchase process. The worst thing to show a user right now is just a spinner while they're waiting to make a purchase.
This should be, and if you can fetch in advance, that's great.
If you can cache on the device that's even better.
Be very cognizant of how you are fetching and preserving these identifiers.
Now that you have the identifiers you need to go to the App Store to fetch the localized product information associated with those products.
This can be done with an SK Product Request.
You pass in the identifiers that you're interested in.
Again, for the smoothest experience possible, you want to do this in advance. You want to anticipate when the user might be interested in looking at your in-app purchases, and have that information available ahead of time. So first thing you do, create your SK Product Request. You pass in the identifiers you're interested in.
You set your delegate. This will give information back to your application about when those products are loaded. And then you just call start. That simple.
The delegate looks like this.
We have product request, didReceive response. This will pass in all of the products that you requested from the App Store.
And each of those products will retain information like a localized title and description. As I said before this is a global market place, you're marketing your in-app purchases around the world.
So it's important to make sure that you're actually using localized information to make your sales. So localized title and description, even more important is price and price locale. You want to present the user the price of your in-app purchases and you want to make sure it's presented in a format that they understand.
Currency is represented differently around the world and you know it's important that you do it right. We'll get into that in a moment.
And then finally if you're hosting your in-app purchase content, this is something that you've set up in iTunes Connect, we actually give information about the download contact length and versions associated with those purchases.
So as I mentioned the price, it's very important that you do the right thing when presenting those price strings.
When you're presenting those prices, you want to use that price locale and pass that into an NS number formatter, and that will get you information about that pricing information. That NS number formatter, will take that price information, take the locale associated with it. If you set it to a currency format, you can make sure that you're getting that pricing information correct.
The other thing you want to do is to not bother doing any sort of currency conversion of your own. The App Store knows how to localize, convert those currencies.
So you don't need to bother with that at all.
Just present the price using the NS number formatter as is.
The next step after that is to present your UI.
Not really going to talk too much about that, these are your products, these are your applications. You know how to present them in your UI to make the best sales pitch possible.
However, once the purchase has been complete, or once the UI has been shown, we're going to jump right into making the purchase itself.
And that can be done by adding and creating an SK payment object and adding it to the Payment Queue.
Once you've created that SK payment object and add it to the Payment Queue, you'll get a callback as the payment is being processed through updated transactions. So this is that Payment Queue Observer you added right when the application started.
You created your Payment Queue Observer.
It called Payment Queue updated transactions. And you'll get information about the payment as it's going through the process so all you did was you created your SK payment.
Added it to the Payment Queue. The information started coming in and you're going to get information about all the transactions in process. You might have one, you might have many. You should iterate through those transactions and then listen to the state for each of those transactions. It's a couple different states, the one you're probably most interested in is the purchase state. This is where you know that the purchase was completed and should go ahead and validate that purchase, we'll get into that in a little bit, and make that content available.
However, there's one other state I'd like to talk about as well and that's the deferred transaction state.
Deferred was added back in iOS 8 and it's for the Ask to Buy feature.
This was to allow iCloud families where children could attempt to make a purchase, such as an in-app purchase, and their parent or guardian, their approver, will have to decide whether or not that purchase will go through.
The deferred state is the state that you're in-app purchase lands on when it's currently pending approval from the parent.
One thing we've seen a lot and we want to make it very clear is you should not block your UI if it's in the deferred state. You should handle that in a way where maybe the purchase hasn't even occurred yet, just let the child who's actually attempting to use your app make that purchase, just let them go back and continue to use the game or whatever it is they're interested in.
Because it might be hours, it might be days before the parent goes through and approves that purchase.
So no spinner, no modal dialogues.
Just treat deferred state as if the purchase hasn't even, you know hasn't even started yet. One thing I also want to get into is it's very important to test your applications when using in-app purchases, just like testing your application any other way.
And this can be done through the iTunes store Sandbox environment. This is a special environment.
If you're running your application and it's a developer signed application, any purchase that you make will go to the Sandbox environment.
However, if you're interested in testing deferred transactions, there's really no good way of creating a family so that you can actually test the Ask to Buy flow.
So if you want to test your deferred transaction in Sandbox, it can be done using SimulatesAskToBuyInSandbox.
This is where you create your SKMutable payment object.
You pass in that product and then you set the SimulatesAskToBuyInSandbox flag. This is a flag that's going to tell the App Store, hey, treat this as if a child was buying this and that child was part of a family.
Once you add it to the Payment Queue that gets sent up to the App Store and the App Store will respond back with a deferred state to your application. This is your way of knowing, hey my app is going to work when kids are using it.
We also should talk a little bit about handling errors. Not everything is going to go right. There's going to be error states in your application.
Especially with in-app purchases.
One thing to keep in mind is not all errors are created equal. I really encourage you to check out the In-App Purchase Programming Guide, or the StoreKit Framework Reference Guide to get a good sense of all the possible errors that can be returned in your application over time.
One thing to keep in mind is most of these errors aren't something that you need to inform the user about. Most of these are just errors that we're informing you to handle in your application.
A great example of that is I'm going through, I'm using your application, I'm browsing around, I'm doing a little bit of window shopping. I click on purchase.
I'll get the price confirmation dialog that StoreKit will show. And then I change my mind, I want to go back and see what else is there. And I hit cancel.
Cancel error, StoreKit will return a user did cancel error to your application.
There's no need to present the user with a user did cancel alert.
And we'll see this in applications from time to time. The user knows that they cancelled.
There's no reason to repeat that back to them.
Let StoreKit handle the transaction flow as much as possible. Again, we got into the Payment Queue. The Payment Queue is where the payment will be processed.
The App Store and StoreKit for you will confirm the purchase with the user. They'll present a price.
That price should match what was inside your app. So that's why isn't important to make sure you localize the pricing information correctly.
We'll authenticate the user.
If there's any issue with their billing information, we'll handle that.
No need for you to pop a confirmation or do anything special, let StoreKit handle that for you.
So we talked a little bit about before the transaction queue and we talked about the states that can be returned for your payment.
Once your payment has made it to the purchase state, you have a number of choices you need to make in your application.
Specifically, around validating that that purchase is backed by a real monetary transaction.
So we'll get into a couple different ways of doing this.
But again, it's your application, they're your products. So it's really important to keep in mind exactly what technologies you're using and what level of security you want to use to validate your purchase.
So receipt validation, this is the receipt that is returned to the application at the end of the purchase. It's just like a bill that a consumer might get when shopping at a mall or something like that. It includes all this information about the purchasing of the app and the purchasing of your in-apps.
And there's kind of two main ways that you can attack validating that receipt.
First is on the device itself.
This receipt comes down, you can write code right there on the device to pull it apart, inspect it, make sure you know authenticate that it came from Apple, make sure that it's from a valid monetary source.
And this can be done kind of generally is for just unlocking features and content right within the app. So again, this is one of the choices you have to make.
Is this a purely client side, right inside your app type of purchase? Or if you have a server side, you might want to actually have the server validate the receipt because you want that to be the gateway for your content or service. And this can be done by taking that receipt that was sent to the device, uploading it to your server and then having that server send the receipt over to the App Store, there's a backend API to verify that receipt.
And that can be done server-to-server.
One note with that though, is yes the App Store does have a backend API that can take in that receipt and validate it, but you should never send that receipt to that API directly from the device. That is not a secure mechanism and there are limits to how trustworthy that can be from your application.
So if you're going to use the verify receipt endpoint, it should really be from your hosted server, right into the App Store itself.
So let's get a little bit deeper into what the receipt is.
The receipt is a trusted record of app and in-app purchases.
It contains information that will allow you to know that this app was bought by this user on this device, and those in-app were bought by that user on that device.
Stored right on the device, right into the application container of your app itself.
It's issued by the App Store and it contains information in it that allow you to verify that it actually came from the App Store itself.
And again, it's for your app, for that device only.
If your app is running on a different device it will get a very different, you know, it will get a different receipt for a different user.
This is kind of, this just gives you a sense of what it looks like.
Again, stored in the app bundle.
We give you an API to get it. We'll see what that looks like in a moment.
And it's a single file.
Old, old, old versions of the receipt there used to be multiple files for each in-app purchase, but since iOS 7 we've had a unified receipt that contains all this information in one single file.
It also, again, contains certificates and signatures to allow you to verify that this receipt is valid.
We do this through a Public Key Cryptography Standard 7 Container. This is the container that contains all this information in it and this is an open standard. So there's a lot of information that you can use out there on how to work with a PKCS7 Container.
The payload of that container, the thing that contains the actual information that you're going to be parsing out, that's encoded in an ASN1 format, again public open standard.
And there's a lot of great options out there for verifying this, OpenSSL is a very common one.
Sometimes people create their own.
But again, something to stress here is that this is your application, this is your money, this is your business.
So you've got to make good choices on how best to verify this receipt and what technologies you're going to use.
However, whatever you choose, there's some similar processes that you're going to go through. First up, is you're going to locate the receipt in the application using an NS bundle API.
And again, this is what it looks like in Swift. You can call right in that NS bundle, and there's an App Store receipt URL that will be returned by that. This is a local URL, local to the file system.
Once you have that URL, you pass it into NS data and you get the binary payload of that receipt.
Now that you have that binary payload, a couple things to keep in mind.
The certificates on there will have an expiration date, but you've got to be very careful about checking that expiration date.
Just because it was issued at a certain time, doesn't mean that that expiration date is valid for all time in the future.
So if you're checking the expiration date, compare it against to when the receipt was issued from the App Store, not to what the current date is.
It's not like an SSL handshake, where you want to check it every time.
It's only valid for when it was issued, or it's only valid to check the date for when it was issued.
Or it's perfectly valid not even to check the date at all.
But what you do want to do is check up to the root certificate authority on the receipt.
That will verify that it came from Apple. That you can trust it because it is Apple's receipt.
So check up to the root. So again, this just kind of gives you an idea of what it looks like inside the receipt.
The payload of it, that ASN1 format that I was talking about is a series of attributes. You can think of it almost as an NS dictionary. It contains types and values.
And we're going to highlight some of those types and values.
There's more in there than we're going to talk about today. And you can get a lot of that online through our Receipt Validation Programming Guide.
But some of the main ones you're going to want to use are type 2 and type 3, that includes the bundle identifier and the bundle version that the receipt was meant for.
Hopefully that matches what's in your application and you should check it.
So you should check the bundle identifier. You should check the bundle version. And then one thing to keep in mind is you should use hard coded values right inside the binary of your app.
It's a lot easier for me to go in and change the info P list, if that is what you're using to match a phony receipt than it is for me to change the payload of your application to match the receipt. So if you really want to make sure that that receipt is for your application, use hard coded values.
Now that you know that this receipt is for your application, you want to make sure it is for the device that's running the receipt right then and there. And this is a little trickier. What you want to do is take a look at the SHA-1 hash that is returned in type 5.
Type 5 contains this hash. The hash is made up of pieces of information that the App Store knows at the time of purchase. And pieces of information that you know a time of verification.
That specifically is the bundle ID.
So again hard coded value you want to keep in your app, the bundle ID of your app.
The device identifier.
This is a, there are APIs for that. It's a little different on iOS versus macOS, but you want to load up that device identifier. And then this Opaque Value. This Opaque Value is basically it's a little bit of cryptographic entropy. It's a secret salt that is included in the receipt. It allows the hash to change over time even if the bundle ID and the device identifier aren't changing.
You should basically take the Opaque Value that's in the receipt and include it in your hash.
Once you have that, SHA-1 hash compare it to attribute 5 and if they match, you know it was for your device.
And again it's unique to your app on that device.
The receipt also contains information about all of your in-app purchases. So what we see here is a whole bunch of, more than one Type 17. Type 17 is for each and every purchase that the user's made with your application it will include one record for that in-app purchase.
The pay load for Type 17 won't be a string, it will actually be another ASN1 document and that will have its own values inside of it.
So what do those look like? Well 1701, that's the quantity of purchases made. So we talked about consumables before, the user could have purchased 10 bags of coins inside your game, or 100 bags of coin.
That's going to be the quantity, the number of purchases that was made against that particular identifier.
The product identifier, we talked about this before. This is that identifier that you use to load your in-app purchase data from the App Store and what you set them up in iTunes Connect as.
The identifier, this is a transaction identifier. This is a unique identifier for the transaction that was used to make that purchase.
The date of the purchase.
And then finally one new thing, it's not new, but one thing I want to highlight is we also include for auto-renewing subscriptions, the subscription expiration date.
So if you're switching your app over to subscriptions, that field's important to you.
You're going to use that to kind of, you're going to use that to know is this subscription still valid? Do we need to ask the user to, or you need to double check that the user is still in a valid subscription? So that field is very important.
And then one other note. If you have an app that's currently in the Store, and it's a pay for app and you're interested in switching it over to a subscription model, one thing you want to make sure you check is inside the main receipt, the app receipt itself is Type 19.
This includes the original application version that that app was bought with.
So if I bought it as version 1.0, which was a pay for app and in version 2.0 you moved to subscriptions, I want to take a look at that original application, because I want to make sure I treat that user as the paid user that they are and not as the subscription user.
I think it's a really bad experience if the model moves over to subscriptions and you don't respect that they purchased that app originally.
So it's important to keep in mind if you're switching to subscriptions.
A couple other notes about the receipt. The receipt is issued at the time of purchase.
But it actually is also renewable.
But it's only renewable for certain types of in-app purchases.
So when we're talking about consumable and non-renewing subscriptions, they are not going to be refreshed with the receipt.
So consumable and nonrenewable subscriptions only appear once. They appear right after that transaction was made. And they're not going to be there when the user refreshes that receipt either on that device or when they switch to a different device.
However, non-consumable and auto renewing subscriptions will always be in the user's receipt.
And you can get those back and should get those back via a StoreKit API. We can take a look at what that looks like. It's a little different on IOS versus macOS.
On iOS, the receipt doesn't exist or is invalid, you're going to want to use this StoreKit API to refresh it.
But keep in mind that you're going to have to have network. We have to make a connection to the App Store.
We have to validate the user.
We've got to know that they're the ones who have actually bought this thing. So they're going to have to either enter in their password, or use their thumb print.
And if you're doing anything like refreshing the receipt every time the app launches, which is really not a great pattern to follow, or if you're doing anything where you're checking the receipt is invalid and then fetching it because you know maybe there's something, something doesn't look right, you want to be careful that you don't get stuck in a loop over and over again. If your app crashes and you're constantly checking your receipt on launch.
Or if you keep checking and it's invalid and there's something not quite right about your validation code, you don't want to validate and fetch that receipt over, and over, and over again. So that's something to be careful about.
This could be done through again, on iOS, this can be done through an SKRecipt Refresh Request.
You set your delegate and you call start.
This will inform your application right on the delegate when the receipt is updated and you can go and process the transactions in that receipt just as if they were purchased by the user.
On macOS a little different if the receipt is invalid, you're going to want to exit with code 173, this will tell Finder, hey go fetch the receipt for me. Again, it's going to require a network and the user's going to have to be signed into the Store. And this can just be done by calling exit 173.
And then I just want to re-highlight again with server-to-server validation. If you're doing an auto renewing receipt and you have, an auto renewing subscription of if you have content that's available on the server, you probably have a hosted implementation backing up your in-app purchases.
And so if you're doing server-to-server validation this will allow you to validate on the server side that the receipt that the user got for their in-app purchases were valid.
So again, you take that receipt that was on the device and you upload it to the server.
However, I'm going to highlight it one more time, don't send the receipt directly to the verify receipt endpoint.
It's not secure and you should not be doing that.
One thing to note the verify receipt endpoint returns a JSON payload and that will actually include information like if the receipt you had was maybe a little out of date, you'll get the latest version right back from the App Store. And it will include some information about was it valid or if there was some other state wrong with the receipt. So again, there are documentation online about exactly how the verify receipt endpoint works.
Okay so now we've gotten through that, we've made the purchase, we've processed the transaction.
It's up to you to decide exactly how you're going to verify that receipt and what level of security you're going to put behind it and what technologies you're going to use.
You validated that that purchase was real. That there was a monetary transaction behind it. It's up to you to make what the user purchased available to them. Make good on your end of the deal. And so there's a couple ways of doing this and we talked about it before.
You might just have functionality that's hidden in your app that you're about to unlock.
A basic version of your application. A free basic version that now is a pro edition and you just need to set that state and make that available to the user.
Or if you have content that's coming down from the server, you need to download it.
Whole bunch of different technologies, whole bunch of different ways of doing that. I want to highlight a few of them now.
One that's a little newer that Apple provides is on-demand resources.
This is a way of taking your application and having the initial download be as small and compact as possible. Just the binary. And if you're going to have other resources associated with that application, if your games have a bunch of different levels and they have you know graphics or videos associated with them, you can break that up into different components within your apps manifest.
On-demand resources is hosted in the App Store, so you don't have to worry about hosting.
It's scalable and reliable. We have caching all over the world for this.
Your users will be able to get their content fast.
One thing to keep in mind is it can contain any type except executable code. So all that code has to be right in your original binary, but it can contain any other content you might want to have. And it's also available only on iOS and tvOS. macOS doesn't currently support on-demand resources.
So that's a technology you may choose to use to fetch additional content after a purchase has been made. If you're interested in learning more about it, I highly suggest you check out Optimizing On-Demand Resources.
It's in Mission, this room, Thursday at 10 a.m.
Another technology that Apple provides is hosted in-app purchase content. This is content that you can associate right with your in-app purchase, right in iTunes connect and again, it's hosted on Apple servers.
So you don't have to worry about hosting them yourself.
Scalable and reliable. Around the world caching.
One thing to keep in mind with this and as with the ODR, is that this will download your content in the background. Your app doesn't need to be running. So the user makes a purchase, and then leaves your app, launches another app, and you have to download this content, that will keep running.
You get up to 2 gigabytes per in-app purchasable product. So that's not 2 gigabytes for your whole application that's 2 gigabytes per product. And this is supported on iOS, tvOS, and macOS.
So, again another great way of having Apple host content for your in-app purchases.
Let's take a look at what that might look like a little bit. A payment came through, the user, you validated it and you're ready to download content.
You're going to be in the updated transactions call back of your payment observer. And you're going to go down a little bit and you're going to look at your transactions and you're going to notice that one of those transactions contains one or more downloads. And all you've got to do is pass that download right back into the Payment Queue and that will kick off the download.
If your application is interested in using hosted in-app purchases, you're going to want to have a Payment Queue updated downloads method inside your Payment Queue Observer.
And this will give you all this information about the downloads as they come in, including things like progress will call in and let you know progress and you'll be able to use this to update UI. Or time remaining.
Or if an error occurs and you have to react to that, that information will be there.
And then once the download is finished a URL will be provided, again that's a local path URL, where it is on the file system, so you can grab it and interact with your content right then and there.
So that's how hosted in-app purchases work.
But you may also want to self-host this content, perfectly valid thing to do. If you have a catalog that changes over time, of if you have different things you don't necessarily want to have Apple host, perfectly valid to do self-hosting content.
One thing to keep in mind is there are background download APIs that you should be using NSURL Session. It's the way to host this content.
Content is downloaded even when your app is not active. If you're still using NSURLConnection that's deprecated.
And that's not going to be as great of an experience for the user as NSURL Session.
So it's very important that you use these APIs.
This is how it looks in Swift.
You're going to create a session configuration.
This is a configuration you want to name is something unique to your application for what that session is all about.
You're going to create your session. You're going to pass that configuration in, you're going to pass in a delegate, this will be whatever object you want to get information back about those downloads. And you're going to give the queue, this operation queue is actually what's going to call into your delegate. What thread or what operation queue is going to call into your delegate. So you're going to want to make sure you create a dedicated operation queue for that. And then to kick off the downloads, you create a task and you hand in the request and URL associated with you in-app purchase.
And you call resume.
That will kick off in the background your self-hosted in-app purchase download.
As the download is occurring your delegate is going to get information, it's going to get called into URL session didWriteData. And that will give you the ability, you can take this information coming in and it will give you the ability to update UI. You might have a progress bar, or something that you want to tell the user about how your downloads are going.
But it's also important to keep in mind that your application may be exited during the downloading process. And so when your application is re-launched, you want to actually reattach to those background download sessions.
And so in your application delegate, you're going to want to implement handle events for background URL session.
This will allow the operating system to wake up your application and tell it about hey this background download just completed, or is in progress, or something's going on with that.
So handle events for background URL session. It will pass in the identifier for that configuration that you used earlier.
You just recreate that configuration with that identifier.
Re-create a session that will re-attach to that background session. And then what you want to do is there's a completion handle that's passed into that app delegate method and you're going to need to call that completion handler once you're done doing whatever you need to do with that download. So if the download's complete and you need to move it into your container, and unpack it, and do work, you need to call that completion handler once you've done everything with it and let the OS know that you're done doing what you need to do.
So that's making assets available, but you're not quite done yet. Even though you've made all this available and the user has what they bought, you've got to do one more thing and you've got to tell StoreKit that hey you're done with the transaction. This is important because we keep that transaction in our purchasing queue, we keep that in our state machine and if your application dies and gets relaunched or if the user leaves and comes back, the Payment Queue is going to keep telling you about it.
It actually might continue to do it on your behalf background API calls, so that's going to use up user's data.
So it's important to keep things clean and finish up the transaction when you're done.
But you should also make sure that you do it when the content is finished downloading.
If you finish the transaction and then kickoff the download, we're not going to tell you about that transaction ever again. So if the download fails, or if something goes wrong, you're not going to get updates on it. So you want to make sure that the download is complete and then call finish transaction.
So, again, the payment will stay in the queue until you finish it. And if you're downloading hosted content, another thing to keep in mind is yes you need to finish downloading it and if you finish the transaction before the download's complete we will stop downloading.
And this can be done simply by calling SKPayment Queue, Finish Transaction and passing in that transaction right into the Payment Queue.
So, the user has been using your app, they've been buying in-app purchases, they love your content, and they decide, you know what I'm running out of space, I'm going to delete the app, maybe I'll re-download it later.
Or they switch to a different device.
It's always important that they can get back those non-consumable and auto-renewing in-app purchases right back into the app. They need to be able to restore that content.
So restoring transactions allow the user to restores non-consumable and auto renewing subscriptions.
Consumable and non-renewing subscriptions, that's up to you to manage the state.
So it might be something that you just save locally, it might be something that you store in the Cloud, or you store in your own hosted backend.
That's up to you guys to manage how exactly you want to restore those, but for non-consumable and auto renewing, you can restore those using the restore APIs.
Again, we offer APIs for that SKPayment Queue Restore Completed Transactions.
That will pull down a brand new updated version of the app and in-app purchase receipt.
Again, you're going to want to observe the queue. You added that to your app delegate, so of course you're observing the queue. You're getting information about those transactions as they're coming in.
Because we're going to call Payment Queue Restored Completed Transactions Finished. That's going to tell you all the restores that occurred and you're just going to want to process those as if they were just purchased by the user.
Or we're going to give you an error that something went wrong and maybe you need to ask the user to try it again, or update your UI accordingly.
So again, it's important to observe the queue.
Again, once the restore is completed, you take a look at the receipt and unlock content and features accordingly.
So now we're going to get into a little bit of okay you're built your app, you're ready to submit it to the App Store you want to make sure that you pass app review.
A couple things to keep in mind.
We've just been talking about restoring.
You really need to have a restore mechanism. It doesn't have to be a UI button, you need to have some mechanism inside the app to allow the user to get their content back, or else it won't pass app review. Again, for non-consumable and auto renewing subscriptions, you're not going to get other types of in-app purchases back on restores.
One thing to keep in mind though is we want to keep this separate from purchases itself. Restores are free.
The user shouldn't get confused that if they do a restore and they're getting a password prompt from the App Store that it's going to cost them anything. This has got to be very obvious inside your application that this restore operation they're doing is not a purchase, it's completely separate from that. So it's one thing you want to make sure your UI reflects.
Some other information you want to keep in mind for auto renewable subscriptions are about policies, keeping the information in marketing text.
And making sure that once they've subscribed that they get that content right then and there, that they don't have to wait until the next period begins for anything to appear. If you subscribe you should get some information right away.
And you should get the information that you should only be able to get through a subscription, not bundled products or content that they would have been able to get free elsewhere. So, these are some of the kind of policies. I highly recommend that you check out the App Review Policy Guidelines.
I've heard there's even a comic book version of that now, so I would highly recommend you learn all about these, because they're changing all the time and you really should keep up to date with what's going on with that.
And with non-renewing subscriptions there's some other policies you want to keep in mind, about privacy and making sure that he user has the ability to opt in or opt out accordingly.
And then finally the most important thing to pass app review is if you have purchases inside your app they should work. You make the purchase, the content should appear. They should get that information, they should get that content right then and there. If it's buggy or if it's error prone, or if there's something going on when the reviewer you know is reviewing it inside that app review environment, you're going to have issues with app review right then and there. So test it.
Test it in Sandbox, make sure everything works as you want it to.
So just to summarize a little bit of what we went over today.
Always observe the Payment Queue. I can't stress this enough.
If you're interacting with in-app purchases in your app, one of the first things your app should do is set a Payment Queue observer to get information about what's going on with the payments. You need to fetch localized product information from the App Store. This is through an SKProduct request.
That will return localized information. So the information for the users in their country related to billing that's associated with their App Store account.
And again use the product's price locale.
There's the SKProduct that comes back, has a locale associated with that, you need to use that for displaying the pricing and currency information.
Use the receipt to validate your purchases. Again, they're your purchases, this is your business. You need to make decisions about how best to validate the receipt, whether you do it locally or on the server. What technologies that back it. Be aware exactly what the pros and cons of each are and make your decisions with that.
And finally make the content available. You can just unlock it in your app right there on the device, or you can make a hosted request from the App Store itself, or you can make a request to your backend hosting APIs.
Make that content available. But do it in a reliable and quick way. If you're hosting it yourself, make sure your servers are scalable and you know cache appropriately and are, you know, really ready for users making great use of your app.
And then finally, always finish the transaction. The last thing you need to do is make sure that that transaction is no longer in the Payment Queue or else your observer is going to continually be notified about it when your app starts up. And that will incur weird you know possibly the user might have to authenticate at certain times, or there might be backend APIs that are occurring on behalf of your app, using up data for the user if you don't finish that transaction.
So always do that.
And then, allow users to restore completed transactions. You need to have a restore mechanism inside your app, if you have non-consumable or auto renewing subscriptions. This will allow the user to get back what they've already purchased.
More information will be online at the developer website. I highly recommend you go back and take a look at that later on. And then a couple related sessions we mentioned today, if you're using subscriptions in your app, you should definitely check out Introducing Expanded Subscriptions in iTunes Connect later today.
Or if you're interested in on-demand resources check that out Thursday morning. And that's it.
Thanks a lot.
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.