Services • iOS, OS X • 52:08
Using the StoreKit framework properly and optimizing the user experience of your In-App store is vital in order to never miss out on a sale or confuse people. Learn valuable techniques for a trouble free browsing and purchasing experience, and to ensure your app gets through the app submission process smoothly.
Speaker: James Wilson
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Good afternoon.
[ Applause ]
Thanks. Hi. My name is James Wilson. I'm an Engineering Manager and one of the things that my team works on is the frameworks that power the App Store and the iBook store in OS X, which of course includes StoreKit. And we've run a StoreKit session at WWDC over the last few years, pretty much since we debuted StoreKit for doing In-App Purchases. But the session has typically been aimed at beginners and intermediate level for developers to get a feel for how they can implement In-App Purchases using StoreKit.
We know obviously from the absolutely massive uptake of In-App Purchases that it's time to move on from that. So this year I wanted to do something different. What we're going to focus on in this session is how to optimize your In-App Purchase implementation. And the kind of optimization we are looking at is not so much using less memory, using less memory, making it faster etcetera. What we're specifically looking at is optimizing your implementation to be trouble-free, reliable, smooth, and a great experience for the user every single time.
But if you're not familiar with StoreKit, here's the three key things that StoreKit does for you. Of course we all know and love that StoreKit allows us to do In-App Purchases in our apps. This is for both consumable and non-consumable items, as well as for subscriptions. One other thing that StoreKit does is it has this thing called the StoreKit product sheet, which is a way in which you can sell apps and other content from the iTunes or the App Store through your app. And lastly it can be used for receipt renewal. Now receipts are a really powerful way that you can enforce your business model, prevent piracy, etcetera, right within your app as well as within your service. And StoreKit's involved there as well.
Before we launch into optimizing In-App Purchases using StoreKit, I wanted to cover a few things that are new this year. The first is that that StoreKit product sheet that I just mentioned before, it allows you sell content from other developers or other apps or content you have in the store through your app. That StoreKit product sheet now supports the affiliate program.
This is really great for you as a developer. We debuted the product sheet, I think it was last year. But at that time it was simply a means by which you could market and sell those other product and content through your app, but now with the StoreKit product sheet supporting the affiliate program, if you're part of that affiliate program you can get paid for those items that you are selling through your app.
The second new thing that I wanted to call out is that in StoreKit we're introducing a new transaction state called deferred and we'll look at a bit more in a second, but this has come about due to the ask to buy feature that was implemented as part of our family sharing feature.
So family sharing allows you to add up to six members of your family and form a family, and that family can share purchases and a payment method. And when you have children as part of that family that are under 18 you can enable ask to buy on them so that they can ask for permission to buy things from the store, as well as In-App Purchases in your app. And then the parent can approve that purchase remotely from their devices. So when the transactions in that state we call it deferred. And that's why we introduced this new transaction state.
We'll touch on this briefly now and I'm going to look at an example of how to handle it and what sort of things you should do in your app later on. But it's called SKPaymentTransactions StateDeferred. What this means is when you see a transaction into the deferred state it means it's neither purchased nor failed, yet. You will receive a further update about this in the future as in the transaction will enter that final state of either purchases or failed, but it will be an indeterminate time between when the purchase begins and when it enters that final state.
The most important message I want to get across for you as app developers is that to implement support for handling this deferred transactions state you need to engineer it in a way that your app can still be used by the user while it's in that deferred state. It's also worth mentioning that we do actually support and allow you to repurchase that item, that is attempt to make subsequent purchases for that item while the permission is still pending, or while the transaction's in that deferred state.
Wherever possible though, especially in regards to the deferred transaction state, you should let StoreKit handle the user interaction. We'll handle a lot of the messaging and dialogues that are required for the user in regards to the Ask to Buy feature. This is how ask to buy works, so I've got a child device here and a parent device.
When a child wants to buy something such as one of your In-App Purchases, they attempt to make the purchase by pressing the Buy button and they'll be prompted to ask permission for that item. That causes a message to be sent via the App Store to the parent's device, in fact all the parent devices or approver's devices.
And they receive a notification that this request has been to buy this item. When it enters that state, that's when the transaction goes into the deferred state. So once the child has tapped the button to say yes they want to ask permission, and the notification is sent out to the approvers' devices, that's when the transaction becomes deferred.
Now once the parent goes ahead and approves or declines this purchase request, we'll send a message back down to the child's device or the requester's device. And at that point, that's when the transaction will enter the familiar purchases, or failed states. Now let's talk about how to optimize your In-App Purchase implementation.
This next section is what I call stock it in 60 seconds. It designed to give you a really brief introduction to the processes involved in completing an In-App Purchase using StoreKit. The first thing you've got to do is know what you're going to sell. You need to load those In-App Purchase product identifiers. Then once you know what you're going to sell, in terms of those product identifiers, you need to ask the App Store for the localized information about those products.
And we do that with SKProduct request. That gives us back a bunch of SKProduct objects that we can then go and draw our really beautiful In-App Purchase UI. Once we've shown our beautiful In-App Purchase store UI and we've enticed the user to make that purchase, we proceed on to converting that product into an SK payment object and adding that into the payment queue.
The payment queue will then give us updates about how that transaction is progressing and we will need to process that transaction as it moves to the purchase or failed state. Assuming they purchase the item, we then go ahead and make that asset or that content and feature available to the user. And then lastly we finish the transaction. So that's it. It's a pretty simple process when you string it together like that.
What we've noticed though is though from all the apps that we see submitted, from all the apps I've used personally, from the feedback we get from developers and tech support, as well as from meeting a lot of developers here at WWDC is we know that there are definitely some danger zones in here.
There's areas of this process where the problems can crop up, or there's common pitfalls or got you's. So the focus on this session is to look at those, and to help you get over those. To really bring your In-App Purchase implementation up to the next level. We want it to be as smooth as possible, a great experience every single time. To start off with let's look at the user interaction in particularly in the pre-sales piece of making an In-App Purchase. That is when you're showing your store UI.
Remember the first thing we have to do is know what we want to sell. We need to load those In-App Purchase product identifiers from somewhere. Now these are the product identifiers that you set up in iTunes Connect when you defined your In-App Purchase saleable items. So there's two options for how you can load these product identifiers up.
One is if you've got a very static catalog of items that you're selling, that is you might only have a handful of things you're selling and you know they won't change throughout the lifetime of the app, then it might be simple enough for you to just bake those identifiers directly into the apps binary, or include it as a pay list that's part of the app that you submit to the store. That's the simplest implementation.
For a lot of developers though, we know that that doesn't suit you well enough in terms of giving you enough flexibility and what we see is that a lot of developers, the first thing that happens during that In-App Purchase flow is they make a call to their own service to fetch that list of product identifiers that they're going to display to the user.
Now if you're doing that, you need to think really carefully about how you're caching and loading that data. Because it's the first step in making an In-App Purchase. And that In-App Purchase process is such a fleeting moment of trying to transform the customer's intrigue and interest into a sale that's obviously a benefit to you, but is also to help with their enjoyment and usefulness of the app that they're using.
Even more important is the reliability of those servers that are hosting your list of products that you're going to sell. You've got to make sure that if you're going to have them on server and you're going to make an HTTP request or whatever it might be to load them, that you have a super-reliable, font-tolerant, really performant server platform that is able to issue those product codes really, really quickly.
Because that's not the way to start an In-App Purchase. So often when, if I'm playing games, or I'm using an app and I'm enticed to make an In-App Purchase, probably because I'm not doing very well at it, I tap the button to see what it's about and I'm stuck at a spinner.
There's no need for that. If you have to make a network request in order to enter that In-App Purchase flow, anticipate it and do it ahead of time. For most of you, you know when it's likely that the user is going to be presented with these In-App Purchase offerings, so just ahead of time do those network requests you need and avoid them getting stuck at a spindle like this.
So how do we load that product information? You do that -- well sorry once you've got those identifiers, you've got to load the product information about them. This is when you transform that simple identifier that you defined in iTunes connect to getting the localized product information, product price from the app-store that the user is signed into. And you do that using SKProduct request.
You pass into this the set of identifiers for which you want that localized product information back. Now when you do this, as I said just before, always try and anticipate the presentation. This will be another round trip on the network. There's always, always going to be some delay there.
So it's best if you can anticipate this, load it just ahead of time, or at a point in time when you know that you can safely do that without interrupting the user. So that you can present that in-app UI really quickly. Now what you get back when you make that SKProduct request is a bunch of SKProduct objects. The SKProduct object will contain the localized title and description, the price and locale, and if you're hosting In-App Purchases with us, then you'll also get back the content size and version.
The most important bit in the SKProduct object you get back in terms of making for a really good In-App Purchase experience, is that localized title, and the localized pricing information. You would be amazed at how many different ways there are to represent the simple concept of currency around the world.
This is just a very small subset of examples. This is all different ways to represent 1234 and 56 in currencies around the world. Now currency is something that's always near and dear to people, especially when you're offering them something for sale. So make sure you can present the currency for what you're offering through In-App Purchases in a way that's comfortable and familiar to that user. It makes for a much, much nicer experience to make that user feel like they are first-class experience in your app, regardless of wherever they are around the world.
Here's a neat trick for doing that. You can show that you can localize price using NSNumber formatter. Here I've created my NSNumber formatter and I've set the number style to be in currency, because that's what we want to display, but here's where the magic starts. I set the locale for that number formatter, to the price locale that I got back from the SKProduct object.
I don't make any assumptions about how the device is configured, I don't make any assumptions about where the user might be based on any other criteria whatsoever, because we want to show the price that's correct for the App Store that they are signed into. And you do that by using the price locale that you get back from that SKProduct object.
Now the second piece to this puzzle is when you go to create that formatted string by calling the string for number, is you should pass into that the price that you get from that SKProduct object. You see that SKProduct object has everything you need to present this item perfectly suited for the user no matter what region or what country they're in. So use it to make sure that it's the most comfortable and familiar experience for the user.
But really importantly is there's absolutely no need and you definitely should not be doing any currency conversion yourself. When the user signs into the store with their account in their region or country, the App Store is automatically set up to deliver you the pricing information that is correct for the region they're in. Don't try to convert currencies yourself.
Another thing that I often see go wrong is how developers handle errors especially during this really important pre-sales part where you're making the pitch to the user about what they want to buy. The important message here is that not all errors are equal. I really encourage you to checkout either the In-App Purchase programming guide, or probably even better, the StoreKit framework reference guide. Because it will have a fairly small and easily digestible list of error codes that the framework may vend to you.
Now errors can happen at a lot of different times. And sometimes that error is something you, the developer, needs to deal with and message to the customer, but more often than not it's an error message to just provide you feedback at your apps level of what happened during that transaction. More likely than not, we've already told them what happened. We've already presented the dialogue to the user. We're already trying to take them through the process of rectifying whatever might have gone wrong.
So make sure you know what those error codes are. Check what the code is and know for sure what errors you need to message the user about and what you can rely on StoreKit having taken care of for you. And the most popular example that I see of this and the one that gets me every single time I use an app that it does this, is when I'm seeing something I want to buy in your app, tap the Buy button, I'm all excited about it.
Dialogue comes up to sign into the store or touch ID. I've done that and then I get the dialogue to confirm my purchase. And maybe for whatever reason I've got a bit of cold feet and I hit the Cancel button. So many apps I see then follow that up with a second dialogue that says purchase failed, you cancelled the transaction.
I know I cancelled the transaction. I cancelled it. Okay? Yes your app got an NS error back that told you that they cancelled the transaction, but you really don't need to go reinforce the point to the user that they got cold feet and backed out of the transaction.
In fact, it can be really damaging for your, you know, possibility to keep that momentum going and make that sale. Because if the first time through a purchase I'm not sure about it and I decide to back out, if my experience there wasn't great, then I'll tell you what, I'm really not convinced that I should be spending my money on this app if they can't make a good job of letting me back out of a purchase.
So just being aware of that. It's one of a really good example of where it's important to know exactly what these error codes mean. Wherever possible just let us -- let StockKit handle the transaction flow as much as possible. We see an increasing number of apps starting to introduce dialogs and messaging ahead of the In-App Purchase flow. If that's something you feel you need to do, that's fine, but I'd strongly encourage you to come along tomorrow to a session given my Chris Espinosa at 3:15 about apps and kids. It has some really good tips there about how to handle this sort of interaction.
So now let's move on to making the purchase. You have done a great job of showing this glorious In-App Purchase UI to the user. It was perfectly presented to them, no rough edges, no silly error messages. They've tapped the Buy button and they want to move forward. So when you go and make that purchase, this is when you start to really get into using StoreKit. StoreKit revolves around and is centered around a payment queue. And the most important thing you've got to do with that payment queue is observe it always.
The payment queue is where you'll get all information about how a transaction is progressing, whether it was purchased or failed, whether it's in progress, whether it's deferred. You'll also get information about restored transactions, etcetera. It should be the center of your In-App Purchase implementation and it's the only source of truth for state about transactions as they occur. There's absolutely no need for you as a developer to try and integrate some sort of complicated state machine, or caching of state about transactions that are in place, because you can get all of that from the payment queue itself.
You can get transactions that are in progress. You can know about the status of a transaction as it progresses. And if you're hosting In-App Purchases with it, the queue is where you'll also get that download status information. I think one of the most common reasons I see developers attempt to tack on a side cache of state about In-App Purchases, rather than just holy and solely trusting the queue, is because they feel that in order to update UI elements, and you know, communicate the progress of the In-App Purchase flow through the app that they need to somehow stash that payment object away or try and tack some extra state on to it somehow so that they can keep that UI up-to-date and congruous, but in fact that's not true. And we will see in one of the examples I'm going to give you how you can harness the queue and trust only the queue and still achieve all that.
But just as you've got to rely on the payment queue for all those updates and all those pieces of information, you also need to be aware that any and all transactions that appear in the queue are real and valid. Just because your app didn't start a payment or your -- the running instance of your app didn't start a payment or for whatever reason you don't think for some reason this transaction is valid, then you are likely leaving your customers out from a real monetary transaction that has taken place.
So make sure you trust the queue completely. Any and all transactions that you see in there, any and all updates you see are real and valid. Now if you're concerned about how to ensure that they are not an unauthorized or a fraudulent transaction, that's a different matter. Please come along on Friday morning at 10:15, I'm doing a session about preventing unauthorized transactions using receipts and you'll see that it's the receipt that holds that source of truth about whether something was a real, valid monetary transaction. But otherwise for all intents and purposes, we trust the queue.
And how do we do that? On launch, as soon as possible, ideally in your application deep finish launching, you should be calling SKPaymetQueue addTransactionObserver. The object you add in there will be your object that conforms to the SKPaymentQueue observer protocol and that's your object that forms the center of your In-App Purchase implementation. It's going to receive all the updates about how payments are progressing in the queue and it's how you can handle everything from dealing with errors, to updating UI, etcetera. So I'm going to walk you through a quick example here.
Not so much because you need to get this beginner sort of level information, but because I want to show you how it leads into a really bad design pattern and why that's wrong. First thing we've got to do is of course we make this call making to SKProduct request to get information about the products we want to sell.
Now once we've done that and we set our delegate and call start, we're going to get that product information back. We're going to get a bunch of SKProduct objects. When the user taps the Buy button and wants to end that purchase flow, we take that SKProduct object, and we transform it into a payment by calling SKPayment payment with product.
One little thing to note here is I've heard some feedback from DTS in particular is that they've seen a lot of developers try and handcraft their own SKProduct objects to create the payment with. That's totally not the right thing to do. What you need to do is get your identifiers of what you want to sell.
Use SKProduct request to get that localized information and the SKProduct objects. Then you hand those SKProduct objects into SKPayment to get the payment object. Don't try to craft your own. You've got to use those real live SKProduct objects. So the first step was that we call SKPayment payment with product. The next step is we take that payment and we add it into our payment queue to start that process.
It's very, very deliberate that there is no step three here. Because at this point in time you are completely hands-off and you should step back and let StoreKit handle this transaction for you. In fact the transaction will take on a life of its own completely outside of your app.
StoreKit and its background processes will handle the actual purchasing process, the dialogues, and the sign in with the user outside of your app. That means that payment takes on a life outside of your app, even if your app crashes, even if your app is quit, or other circumstances that cause a long delay in that payment process transacting, it will continue to happen even if your app's not running.
So what we need to do is we've got to handle the events from the queue to know where this payment is at. The most important SKPaymentQueue observer method to implement is payment queue updated transactions. This is what gets called every time a payment starts progressing through the process of becoming an actual In-App Purchase that is made.
And we can implement this like this. So here I've got a full loop that loops over each of those transactions that I'm receiving an update for, and for each transaction I set up a switch statement because I want to particularly know which state this transaction is in. Then I can do things like looking for the state of SKPayment transaction purchased. That means the purchase has completed and I can unlock features and content. I can go ahead and check the receipt, make sure it's a real monetary transaction, not a fraudulent one that's occurred, and then unlock those features and content accordingly.
But don't do this. Don't set up that case statement to look for the state of purchased and then try and find a matching payment in some side cache that you've got of payments that you thought were in progress. Because once you've created that payment object and thrown it in the payment queue to start the process, that object should be dead to you. You should forget about it. You'll get information about those payments and those transactions through the queue.
So if you did something like this and tried to fetch that payment out of some cache that you were maintaining of what you thought was in progress, and then even worse, did something like this and said, "Huh?' "I don't know this payment, I've got no idea where it comes from. I'm just going to ignore it and skip over it." You will have left the customer out here.
Okay. If something arrives in the queue, it's because someone bought something. Even if you don't think it originated from your app, or the running instance of your app, there's lots of reasons of why a transaction can suddenly appear in the queue. They're always real. They're always valid. You should process them.
So why not? Why wouldn't we do that? Because what if your app crashes? Obviously then you've lost your cache of information. And not only will the user have to endure a crash in your app, but next time when they launch if you then ignore the subsequent update you get about the payment, then not only have they put up with a crash, they've missed out on what you've sold them.
It's just as valid. Always process those transactions. Now, getting back to what we talked about before. This new state of the SKPayment transaction deferred. Here's an example of how to handle that transaction being deferred. So remember in this state, a child that's part of a family sharing unit has asked permission to buy something in your app. And the transaction has moved from SKPayment state purchasing to deferred.
Now when you see that status come through as SKPaymentTransaction StateDeferred, here's what you should do. There's three important things that we'd really encourage you as developers to do in your handling of this situation. The first is allow the user to keep using the app. It would be an absolute buzz-kill for the kids if they are sitting there in the app, absolutely delighted about the possibility that mom or dad might just say yes to this purchase they've asked for, but all the while they have to sit there waiting.
Spinners up. Waiting for permission. They're going to quit. They're going to go to another app. You're going to lose them. Okay. Make sure they can keep using the app in some way. Because it may be some time before that transaction is updated. It could be up to 24 hours before the approval or decline comes through from the parents.
So really don't get them stuck in that modal purchasing state where they can't do anything with your app. In fact one way to think of it is you've got a very captive audience at this point in time. Someone wants something in your app and they're waiting for approval for it. Make use of that time.
But at the end of any transaction you perform with StoreKit, whether it gets purchased or failed. You always must call FinishTransaction. When you call FinishTransaction that tells the App Store that you're done with this transaction. You've seen it, you've processed it, you don't need to see it again. It removes it from the queue and then it makes sure that we don't keep calling the Updated Transaction Method each time on launch or at other points when your app is running.
Now this is most common cause that I see for bad app behavior on launch, especially things like really slow launch time, or a launch time where it launches, then it's hanging there for a while. Or worst of all when the app launches and I'm presented with a series of dialogues that just don't make sense to me because in fact what's happening is because you didn't remove that transaction from the queue, when your app launches we get -- and you add that transaction queue observer in, the first thing we do is go and look at the history of transactions that are still pending and haven't had Transaction Finished called on them, and we'll go and replay all those updates through to your app.
So if you then start processing them and have reason to message something to the user and still fail to call FinishTransaction that means every single time your app launches, it's going to go through this process. It's a bad user experience and it's one that gets worse the more the user uses your app. So always call FinishTransaction to get those items out of the queue.
Two tips about the SKPaymentQueue object itself. One is going back to this notion that the SKPaymentQueue is your single source of truth. It has a property on it called transactions. And you can call that property and get the list of transactions that are currently inflight. So for example, if you wanted to -- when you show your In-App Purchase UI make sure that you correctly represented any items that are still purchasing. You could call transactions to get the list of payments in progress. As opposed to trying to track that state yourself on the side.
The other one is keep in mind that in-app purchasing can be disabled on the device. And it's a really bad experience for the user if they're enticed to play your game or use your app enough to want to make an In-App Purchase then only to find that they get some error message telling them that that's been disabled. So you can call it can make payments on SKPyament queue it's a class method for returning a BOOL to know whether or not In-App Purchase can actually be made. Now I want to give you a quick demo of a few things that we've just talked about.
Okay. I have here a very, very simple app for OS X that does an In-App Purchase. Everything I talk about in this session, including this demo, applies equally to iOS and OS X. StoreKit is the same on both platforms. But in this app a few things to call out. In my application deep finish launching, first thing I'm doing is I'm adding an object in to observe the queue.
And down here you'll see I will have implemented my payment queue updated transaction method. It's in here that I start inspecting and dealing with all those events that happen as purchases progress through the queue. I deal with things like entering the purchasing state, when things first start, when it gets purchased, where it's failed or restored. So for the sake of this demo, I'm going to run my really, really simple app.
All it does is it sells a .99 cent banana via an In-App Purchase. Really exciting. I'm going to go ahead and purchase that. Now as soon as I hit the Purchase button, before any other user interaction, I've straightaway my breakpoint was hit that I set in my payment queue updated transactions method.
So that's really important to know, because if you were thinking you had to track UI state yourself, this demonstrates you don't. As soon as that button was clicked -- bang. We're into the payment queue and observing an event occurring. And in fact the event that we're observing is the fact that this payment or this transaction has entered the state of purchasing.
So this would be a time when I could go and update the state of my Buy buttons whatever else I need to do to inform the user that yes this transaction is underway. I'm going to take that breakpoint out because that can get a big noisy. But let's let the app continue and see what happens here.
So I've been prompted to sign in. Now one thing to note is because this is a test app, I'm using the App Store test environment. That is I have my app signed with my development certificate as we see here. Scroll down to the code sign. Sometimes it's easy to just search here for code signing.
Of course you need to set it all and combine. See here my code signing identity is set to Mac developer. This makes sure that my app is development-signed when it runs. That means that when we start using StoreKit, StoreKit can inspect our code signature -- the code signature of this app and it can know that because it's development-signed it needs to talk to the test environment, not the production App store. And that allows us to test our In-App Purchase implementation before we have anything on the store. So I'm going to go ahead here and enter my password.
Cool, yep. I definitely want to buy this banana. And I've already purchased it. I've done this demo a few times now. And see here when we get the final thank you through from StoreKit and from the App store, we're already back in in our app, we've got our breakpoint fired in the SKPayment transaction state purchased. This is where we can then go ahead and inspect what it was that was bought by looking at the receipt, unlocking features and content, and then whatever else we need to do based on this purchase having occurred.
But right now, at this point because of the breakpoint, notice that I have not yet called FinishTransaction. I've made breakpoint right at the point when I would normally go and check the receipt to know what they bought and unlock those features and content accordingly. But what if I hit the Stop button up here in my app in X code? This is just like what would happen if my app crashed midway through the In-App Purchase, or what would happen if the user killed your app, or quit your app during the purchase. Or maybe something else happened, lost network, whatever it may be.
The important thing is here -- let's put this breakpoint back on, when my app next launches and this applies to iOS and OS X, watch what happens, on launch I was immediately taken again into Payment Queue Updated Transactions. Why? Because that transaction hadn't finished yet. Your app crashed midway through handling that even though the user had paid for what they've bought. So this is why it's so important to always observe the queue and handle any and every transaction that occurs through here in a way that doesn't rely on you tracking any state yourself.
So this time I'm going to let this go through. And it goes through and make sure that we do actually finish that transaction. One thing I did want to note, we mentioned before about the updating of UI. Let's go through this process again. I'm going to set a breakpoint here on transaction state purchasing. Now when I run this lets buy a banana again.
Just take note of the fact that this time around my breakpoint here was called on the main thread. But you shouldn't necessarily make any assumptions about which thread you will get the call backs on. So if you're updating UI elements in here such as setting the button state, graying things out, anything in UI kit or app kit, make sure you don't make an assumption about which thread that message comes in on and dispatch it to main if you have to to work safely with App Kit and UI kit. Okay, let's go back to the slides. Let's move on to the post sales experience. So we showed a beautiful UI to the user, we enticed them to buy. The buy process was smooth and seamless. Now you've got to make good on what they've paid for.
In particular you need to process the transaction through the payment queue. Then you need to make that asset or content or feature available and this may mean downloading content. And then of course finish the transaction. Now in the post-sale experience there's three key things you're going to want to do.
The first is validate the purchase, verify the receipt from that transaction and you can verify that on device or on a server to server level if you're handing out content from servers. And that is your way to confirm that it was a real monetary transaction that is authentic and trusted and occurred with the production App store. Again, 10:15 Friday come along and let me tell you all about that.
The next thing you want to do is give them what they paid for. And especially if you have to download content for that feature. Obviously as apps become more and more rich in the features they offer, it often makes sense to have a smaller binary for the actual app itself and then download extra content as those In-App Purchases are made.
You can host that with us, or you could have it self-hosted. And then lastly, depending on the type of transaction there may be a need to persist state about the transaction having occurred or be able to handle the situation where the user needs to restore the previous transactions that they have made.
So a quick word on receipt validation. When you do the on-device validation of the receipt, that is confirming that there was a real monetary transaction with the store, you can then go and unlock those features and content within the app itself. But increasingly, like I said we see developers hosting content either with us or on their own server infrastructure that is downloaded when the purchase takes place.
If you have your own servers that are issuing that content out to your users based on In-App Purchases, of course you want to make sure that those servers only hand that content out to real paying customers. And in fact you can to server to server validation of receipts using Apple's online validation service. And that will allow you to restrict that access and be sure that you only hand out those valuable assets to people who have paid.
But what you definitely don't want to do is try and use that online validation service directly from the device. Now there was a time a little while ago when that was kind of okay to do. But when we introduced iOS 7 we introduced the grand unified receipt format.
That unified the receipt format that we use on iOS and OS X. And it empowered you as the developer to be able to validate that receipt on the device itself. That way there was no need for your app to be calling out to Apple's validation service to get back the information about that receipt to know if it was valid. So you should no longer be using that validation service directly from your device.
It's okay to do it from your service to the validation service. But as far as your app running on a device, it should either validate the receipt locally, or pass that receipt up to your service first, then your service can talk to the Apple validation service to know if the receipt is valid. And those iOS 6 APIs they are in fact deprecated now, so you really need to move away from those.
Downloading content, especially in terms of ensuring a very hassle-free, smooth, and great In-App Purchase experience every time, downloading content is a really important area to focus on. At this point in time the user has paid for what you're offering right? They've done their side of the bargain they've paid you.
Now it's up to you to deliver what they've paid for as quickly and reliably as possible every single time. One of the great ways you can do that is by using Apple's hosted In-App Purchase feature. That allows you to host In-App Purchase content with us, it's hosted on our servers, which are very scalable, very reliable.
We can download that content for you in the background, even when your apps not running. So once that purchase is made, the user can switch around to doing lots of other things and then when they get back to your app, the content's there and waiting for them. Great experience for them. And you can have up to 2 GB per In-App purchasable product. That's not 2 gig total per app. That's 2 gig per item you are selling through In-App Purchases.
Now if you're using the hosted content it's so simple to use. We're back now in our payment queue updated transaction method. Looping over our transactions we're receiving updates for. If you see a transaction with a downloads property and that download property contains one or more download and you want to start downloading that content, watch this, simple. You simply call SKPaymentQueue start download and hand those downloads over to us. StoreKit in the background process then takes care of downloading that content and gets it to the user as quickly and seamlessly as possible.
When a download progresses, you'll also receive updates via the SKPaymentQueue Observer Method, payment queue updated downloads. This will tell you things like progress and time remaining, as well as state and error. And when it's finished, you will get a content URL where you can locate where that content has been downloaded to.
Now of course there may be many reason why you want to host that In-App Purchase content yourself and that's okay, but make sure you use the background download APIs to download that for all the same reasons. If you're using the standard old NS URL connection to download this content, there's going to be some serious limitations that you run into particularly around the fact that you can't guarantee the download will start straight away depending on the network conditions, and the user would have to sit there in your app while the download happens to guarantee it completes. That's not good.
But what you can do is you can us NS URL session to do the background download for you. There's lots of great documentation available for this online, but here's a quick run through of how this works. You create your NS URL session configuration by giving it a name, I've called here my background download -- my background session.
And then when we've got our session configured, we create the session itself and we give it a delegate to get updates about how the download's going and a queue on which to receive those updates. That's a dispatch queue. This will be familiar with anyone that's worked with NS URL connection before. You create an NS URL request, in this case I've just created it with the URL that I want to download.
Then simple as this, I create the download task using that request and the download session. You see what we did there? We setup a session to go and download this for us in the background. We told it what we wanted to go and get and then we went and created the download task. And now the OS is taking care of that for us in the background.
You will get updates via the delegate method as to how the download is progressing so you can update your UI. But because these can continue to happen while your app's not running, when your app launches you need to reconnect to any sessions that might be in progress or might have finished while your app wasn't running.
And this is how you do that. In your application delegate you should implement application handle events for background URL session. That will get called on launch so you can re-establish that session configuration, get the session itself, set your delegate again, and then get those same progress updates about how the download is going.
But no matter how you download the content, whether you download it yourself, whether you download it via host In-App Purchases, you must always call finishTransaction of course, but you must do that once you've completely downloaded the asset and made it available to the user. One thing that can go horribly wrong from the user's perspective is if you call finishTransaction earlier, before the download is done, and then something happens during the download, they may have no way to get back, or get to that purchase they've just made. So you call the finishTransaction once the content's downloaded and you've made it available. That way only then does it go out of the queue and only then will you not receive further updates about it.
Now restoring transactions, this is something you have to offer -- excuse me. This is something you have to offer if you are selling non-consumable items or order renewal subscriptions. Now, non-consumable items, they're things like game levels of maps, or run off purchases that are designed to be used across multiple devices.
Order renewal subscriptions, they're like your periodicals where you are setting up a subscription payment for ongoing delivery of new content. If you're offering either of those, you have to make sure you can offer the ability to restore previous transactions. That is allow the user to get back what they've bought before through your app. Especially if they buy a new device or otherwise need to get back what they've already paid for.
But if you're offering consumables and non-renewing subscriptions, so a consumable might be something like gas in a racecar track, gems, or coins, or currency, generally an item that's purchased, then used up, and ideally purchased again, and again, and again. If you're offering those sorts of items through In-App Purchase, then it's up to you, the developer, to persist that state. You can't restore those. Now this comes back to what I was just saying before about making sure you call finishTransaction at the right point in time. Because you get one shot at a consumable and non-renewing subscription.
You have to make sure your app handles the transaction properly, makes it available to the user, and only then calls finishTransaction, because you can't restore those. But for the content types you can restore, you call SKPaymentQueue default queue restoreCompletedTransactions. Now what happens here, is of course, you need to observe the queue because you will receive these delegate callback methods such as restorCompletedTransaction FailedWithError if things don't go well, or paymentQueueRestoreCompleted TransactionFinished.
At that point you can go and inspect the receipt to know what purchases were restored, unlock those features and content; but, note that this requires a network connection and the user will be prompted to sign into the store if we have to verify their identity so that we know exactly what they've paid for to get you that list of restored transactions.
Now just because your app has to offer restore transactions, it doesn't mean you should call it all the time. Because it requires a network connection and because it will require the user to sign in, it should be something you do only when the user asks you to. I know a lot of developers think it would make sense to just call restore transactions every time the app launches, because don't they always want to get back what they've paid for? Yes, but when they have to sign in to do that, it becomes too much of a heavy-weight process on launch. So let the app launch and offer a graceful way for them to restore transactions if you have to do that based on the content types you're selling.
So let's wrap this up with a summary. This is my recipe for trouble-free In-App Purchases. So, when you are loading up your In-App Purchase product identifiers, the list of product identifiers that you're going to sell or offer to the user, be very careful about how you choose to host them and where you host them if you're not going to bake them into your app yourself. Because if that first experience is a spinner while you wait for that to load, or even worse an error because the server can't be reached, that totally derails your sales experience.
Cache it appropriately if you can and avoid delay in presenting these products by fetching just ahead of time if you can anticipate. A great example of this is if you've got a racing car game for example, there's probably just a lot of conditions that you can anticipate around when you're going to offer In-App Purchases. Let's say I'm going to offer an In-App Purchase that allows the user to go faster around the track next time around, or somehow upgrades their car. When are we going to offer that? Well probably when they come in dead last in the race, when they finish last.
So those sorts of things we could anticipate in code right? If we know they are about to cross the finish line and we know that they're in a bad position, just ahead of time go and grab that product information and make sure you've got it on hand for when they cross the finish line in last place and then you can offer them a great way to beat their friends and finish in first place next time around.
Likewise, the same applies when you go and fetch that product information, the localized product information from the App Store. Fetch only the products you need. Sure a lot of you have got 10s, and 10s, maybe even hundreds of In-App Purchase identifiers in your app, but you're probably only going to offer 3, 4, 5, maybe 6 items to the user at any one point in time. And because this involves a round trip to the App Store and network, you only want to fetch just the products you need so that this happens as quickly as possible.
So fetch it just ahead of time, just like we said before try and anticipate when you're going to display this, fetch just ahead of time to avoid any delay there. It just makes for such a smooth In-App Purchase experience if the user can go directly from the point in time when they may be enticed to buy something to being able to tap that Buy button straightaway.
Now when you show your beautiful In-App Purchase UI and I've got to mention earlier today there was a really great session called Designing a Great In-App Purchase experience. You should really check out the video of that. Because it goes far more in depth about design techniques about how to provide a great In-App Purchase experience.
But from a code level, make sure that you ensure proper localization. We deal with a global marketplace. And just because we're familiar with a particular way of representing currency or a particular display style doesn't mean everyone else will like that, let alone want to buy your app if you get it wrong.
So take care to ensure great localization. It makes users feel comfortable and it makes them feel happy that the customs they're familiar with are being enticed and enjoyed by your app. Do not convert the currencies though. Everything you get back in the SKProduct object will be correct for the store that the user is signed into.
And when they make the purchase, that is you take that SKProduct object, make and SKPayment throw it in the payment queue, once you've added it to the queue, step back, and be hands-off. There's no step three, remember? Create the payment, add it to the queue, that's it. Let the payment queue then drive the further updates.
And then process those updates. Make sure you verify the receipt to make sure it's a real monetary transaction that happened with the App Store and that nothing funny's going on. Unlock those features and content as soon as you possibly can for a great experience. And avoid deprecated APIs and unsafe receipt verification that could leave you opened to unauthorized transactions occurring. If you've got to download content to make a feature available, get that asset as reliably and quickly as possible. Use our In-App Purchases if you can. If not, if you're going to download it yourself, make sure you use those background download APIs.
And lastly make sure you finish the transaction. If you need more information, there is -- we an evangelist mailing list setup that you're welcome to email to get help from our wonderful evangelist team. There is also the In-App Purchase programming guide available online and also the StoreKit framework reference. And there's the Apple developer forums that are great resources as well for discussion and help.
There are some related session that I mentioned, Preventing Unauthorized Purchases with Receipt that's me again on Friday morning up in Pacific Heights. That's where we'll take you through how to make sure that your revenue is protected and your business model is enforced in your app and your service. If you care about your revenue come along and let's talk about it.
Also, designing a great In-App Purchase experience. Rachel did an amazing job this morning in here, telling developers how to structure their In-App Purchase UI to deliver a great experience every time. The video should be available shortly. And lastly, Chris Espinosa's session tomorrow, or Thursday. He's really good if your app in particular is targeted at kids. Thank you very much.
[ Applause ]