Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2012-302
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 302
$eventShortId
Shortened ID of event: wwdc12
$year
Year of session: 2012
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2012] [Session 302] Selling Pro...

WWDC12 • Session 302

Selling Products with Store Kit

App Services • iOS, OS X • 47:05

In-App Purchase has proven to be a tremendous way to sell products, services, and subscriptions directly inside your app. With new support for selling products that are hosted securely by Apple, you have even more options for how to deliver your content to users in a highly scalable fashion. See how you can even sell other products like music, movies, books, and apps without the user even leaving your application.

Speaker: Daniel Feldman

Unlisted on Apple Developer site

Downloads from Apple

HD Video (296.9 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good afternoon. So welcome to Selling Products with Store Kit. My name is Daniel, and I manage the Mac App Store engineering team. And Store Kit is the framework on both iOS and Mac OS X that lets you do in-app purchase. And in-app purchase lets you do things inside of your app like sell additional levels to a game or perhaps unlock some new feature of your app.

So if you have a camera app, perhaps you sell additional lenses. Or it could do something like sell additional issues to a magazine. You can do all these things and a lot of other things with in-app purchase. You've probably used an app that does it. Some of you have probably written an app that does it.

What you might not be as familiar with is that at the time of writing, 75% of the top-grossing iPhone apps on the store use in-app purchase for their revenue. So what's the difference between an in-app purchase and an in-app purchase? Daniel Feldman So to be clear, the apps are free in the store, and all of the revenue comes from in-app purchase. So I think it's safe to say it's been a huge success, thanks to you guys.

And the other thing to point out is that in-app purchase is what lets you be flexible with your business model. We understand that selling an app for one flat fee in the store doesn't work for everybody. And in-app purchase is the feature that lets you take another approach. So with that in mind, let's talk about our agenda today.

Daniel Feldman We're going to start with a great new feature I'm very excited about. It lets you sell any iTunes store or apps or content inside of your application, so books and movies and songs, things like that. We're going to move on to traditional in-app purchase, selling your own content inside of your application. We'll focus specifically on the purchase queue and why it's so important to get that right.

We'll then back out and talk about another great feature, App Store hosted content, and we'll wrap up with some best practices. So let's get right into it and talk about selling store content, a new way to sell movies, music, apps, anything that's in the iTunes store inside of your application. Let me show you an example.

You're using an application, something like Shazam, and Shazam deals with a lot of songs. So I found some great song on the radio with Shazam that I want to buy, and you can see there's a button in the middle of the phone that lets me download on iTunes. So I tap it, and I'm taken to the iTunes store where I can buy the song. And that's great.

I bought the song. It works really well, and it's been working well for a while. But we're now out of your application, which might not be exactly what you want. So we have a new solution that we think you'll like a lot more. And it works like this.

Same app, same button, but this time we bring up a new view controller, and it's a little piece of the iTunes store or app store inside of your application. So we have never left your application. What you're seeing here is a song, of course, but you could use this for a movie or another application, anything you want. User taps. That they're done, and they're right back inside of your application.

So what we've seen here is an application that deals with a lot of different songs, a lot of different media. But you might also consider using this for cross-promotion of your own applications. So inside of one of your apps, if you'd like to sell your other applications, you can do that. Let me show you how.

At a high level, first what you do is look up the item identifier for whatever you want to sell. Everything on the store, the iTunes store, the App Store, has a unique identifying number. And we have three ways that you can get it. The first is a search API. That's a web service that you can hit and query it with bundle identifiers, other things, other fields, and get back information about products on the store.

You can also use the Enterprise Partner Feed. It's another way for some of you to get data from the store. The third way is probably the simplest for most of you, and that's simply parsing iTunes URLs. If you've ever gone and looked in Safari at your application and the preview page that we have for each application, you'll notice one big number in the URL.

That's actually your item identifier, and if that works for your needs, you can get it there. So you have the item identifier. The next step is to configure this new ViewController class that we're providing in iOS 6, SKStoreProductViewController. Configure it with that item identifier. Tell the ViewController to load up that page of content. So, those are the steps.

Let's actually look through it in code. You'll want to first choose where you're going to do this, most likely on a button tab, like we saw with Shazam. Simply instantiate that view controller, set yourself as the delegate,

[Transcript missing]

So, what we're going to do is we're going to tell that view controller to load with the parameters that we chose and a completion block. So, it's going to go and hit the network and load up that page. Once it's done that, it'll call your completion block.

Assuming that was successful, All you have to do is present the view controller and it's right there to go. Now, should you want to know when the user is back inside your application, that's where the delegate method comes in handy. So we have this delegate method that you can respond to to know when the user is back inside of your application. Great. So it's as simple as that to sell anything you want in the store inside of your application.

Let's move from selling other people's content now to selling your own content with In-App Purchas. And I want to point out that In-App Purchas is virtually identical between iOS and the Mac. And I'll point out the few differences that there are. But if you have it done in one platform and you want to move it to the other, it's really pretty simple.

There are four types of things that you can sell using In-App Purchas. The first we call consumable. These are used up like tokens in a game or power-ups that a player in a game would just use up and they're gone. Contrast this with non-consumable. This would be something like a feature in your application that you unlock and the user has access to from then on.

We offer auto-renewing subscriptions. You'll want to use this for something like a magazine that has content that's released periodically, new content. Contrast that with non-renewing subscription, which is for something more like a service, like an app that ties into a dating service that you want to give somebody access to for three months, or whatever the case may be. So those are the things you can sell. Let's talk about how to sell.

At a very high level, there are three steps. The first is set up your content. If you're building a game and you want to sell additional levels, well, you've got to make those levels. Once you've done that, test it inside of your app in Xcode, and you'll want to enter information about what you're going to sell in iTunes Connect. So that happens sort of ahead of time, right? Well before the user ever enters your application.

What happens at purchase time is on the device or Mac. That's when the user is actually using your application and goes to buy something. That's where you want to use Store Kit. Finally, well after they've purchased it, subsequent launches of the application, you're going to verify those purchases, make sure they really came from the app store, and that you're not being pirated in some way. What we're focusing on in this talk is the purchase process and what happens in your app. So let's break that up and go through it step by step.

So this whole process, to put it in some context for you, happens when the user is inside of your app, taps on something that says Store, or you otherwise want to present what there is for them to buy. That's where we're starting. Now, the first step of that is to get item identifiers. So just like everything in the iTunes Store, you need to provide a unique identifier for everything that you want to sell in your app. And we'll talk about how to do that. Excuse me, you set that up ahead of time in iTunes Connect.

So, what's the best way to deliver your content? Well, the best way to deliver your content is to use the same identifiers that you set up in iTunes Connect. You can get them into your app in one of two ways. The first is have them in your app. They could be just hard coded or you could load them in from a P list. The second is to talk to your own server.

Then it's up to you to come up with some kind of communication between your device and server to get those. So, that gives you a lot more flexibility if you want to add more things to buy later on. But if you just have simple needs, you might choose to go with just putting them inside of your application.

Next step, we take those item identifiers and we fetch up-to-date information about each of those products. In iTunes Connect, we let you change the price or the title of what you're going to sell at any moment. So immediately before you show the user what they can buy, you need to go get that up-to-date information. That's this step.

Daniel Feldman Take those item identifiers, create an NS set so that we know they're unique, and you can use that to make an SK Products request object. This is what will do the hard work of talking to the server. Set yourself as a delegate and call start, and Store Kit is off to the races talking to the server and getting that information for you.

We will call your delegate back on one of two callback methods. The first one is the success case. We'll give you an SK products response object. And that object will have two properties that you'll probably be interested in. The first is a products array. Each item in this array is an SK products object with a lot of metadata. It'll have the description and the name of the price of whatever it is for sale. And the other property is the invalid product identifiers array.

Now, if you had a typo in an identifier that you sent us or forgot to register it with the iTunes store, something like that, it'll be returned here so that you know that there's some issue with it. If there's an error with the request overall, like you're not connected to the internet, for example, you'll get an NSError back in this delegate callback method, and you should respond accordingly in your UI.

So we have that up-to-date information. Now is the fun part. You get to present whatever it is you want to sell inside of your application. This is entirely up to you. It's your responsibility. And the reason for that is it needs to fit your app. You might have a really immersive game that takes the user through forests, and they're fighting animals, and they find this tavern where they can buy new swords and shields. Well, Store Kit can't provide that for you. We need you to create whatever makes sense inside of your application.

If you have an app that's more conventional, it could be a more conventional list of things that are for sale. But I do want to impress upon you that this is an opportunity to really sell, not just show what's for sale, but sell it. Make it look really great.

If this is where your revenue is coming from, all the more reason to put a lot of effort into this UI. So the user has chosen what they want to buy. How do you actually buy it? Well, take that SK product object that we got two steps ago, and you're going to use it to create an SK payment.

Simply take that payment and add it to the SK Payment Queue, and you've started the purchase. SK Payment Queue is a class that we're seeing for the first time right now, and we're going to come back to again and again. It's really the backbone of In-App Purchase. So from there, you've actually started the purchase. The user experience once you've done this is as follows. We'll put up a confirmation dialogue. This is what it looks like on the Mac.

Assuming they click through that, they're asked for their login information, and they can log into the store. Assuming they remember their password and all goes well, we're ready to process the transaction. So the server is going to tell Store Kit everything went great. Store Kit is ready to tell your application everything went great.

But we didn't set a delegate here. How do we know what in your application should be told that the purchase was successful? Well, there's actually a step that's at the very beginning of the process. Actually, before the beginning of the process. It needs to be done on the launch of your application.

We're talking about it now because it's coming into play. But I want to impress upon you that when your application is launched, this is when you should register an observer on this SK Payment Queue. We'll talk in just a moment about why it's important to do it at the launch.

So whatever object you choose implements a protocol. The most important callback is this updated transactions callback. We're going to talk about this a bunch starting now. So this transaction was just successful. The user bought something. We're going to call your observer method here, and we're going to pass back an array of transactions.

Now, if the user just purchased one thing, there's going to be one transaction. But we'll talk about other cases where there might be more than one. So it's best to loop through it. Each transaction has a state. There's four possible states. We're going to talk about two at the moment. The first is purchased. That means everything was great, was successful.

It could also be failed, in which case the transaction will have an NSError, and you're going to want to respond to that in a manner that's appropriate for your UI. But let's say that it was successful. We're now ready to make the asset available. You have two choices to do this.

The first one is simply to unlock something inside of your application. The functionality that was already there, the purchase is basically just giving the user access to it. And the second is to download additional content from your server. Maybe you have a cooking app and they bought some recipes and you need to go and get the files from your server that represent those recipes. So whatever makes sense for you to do, these are the options that you have right now. Daniel Feldman The third one is to always finish the transaction.

SK Payment Queue has a method called Finish Transaction and you always need to call it. If you find that every time you launch your application, you're being prompted to log into the App Store, it's probably because there's something stuck in the queue and you didn't call Finish Transaction on it. So it's really important in all situations to call Finish Transaction.

So that's the purchase process, and that's what happens when the user wants to buy something. There's a concept called Restore that I want to talk about now. And that's not to be confused with restoring a device. This restore is a way for users to get back all of their content.

So this is especially important when the user redownloads your app from the store or in multiple device scenarios. So I bought an in-app purchase on my iPhone, but my iPad doesn't get that content automatically. I need to do a restore on my iPad to get it. So applications really must offer this. The user needs a way to get back all of their content.

That said, consumables don't come back when you restore. These are things that were used up, presumably. So it's only non-consumable and auto-renewable types that come back. And the last thing I want to mention is it's best not to do this automatically. This will cause a prompt for logging into the store, and you're going to want to let your user initiate this because of that.

So that being said, let's look at how we do that. The last three steps are exactly the same as they were for a purchase. And the first step is very simple. You just kick off the restore with one method on SK Payment Queue, Restore Completed Transactions. From there, it's basically the same. Same callback, updated transactions. You're going to want to loop through it, check that state. But this time, the state will be restored if it was successful.

So that's just a little clue that if you want to do something different between when something's purchased and restored, you can check the state and know that it was restored. Same two options for actually giving the user their content. You can unlock something in your app, or you can go and pull it down from your server. Same last step, finish the transaction.

So now we've talked about all the code that you need inside your application to do this. That's it. What I want to talk about next is testing it. And that's where the sandbox environment comes in. So the production environment is what you think of as the app store. It's what users are buying your in-app purchases from when they go and get your app from the app store.

We have what is essentially a copy of that that we call the sandbox. And the purpose is that you can hit it and have real transactions and buy things as much as you want without using real money. Sounds amazing, right? So you're working on an app, either on an iPad, iOS device, or your Mac.

How can you get it to talk to the sandbox? Well, StoreKit knows which environment to point at based on the signature on your app. So if an app comes right out of the store, it's going to point to production naturally. If you've signed it with your development cert, we will automatically point to sandbox.

I do want to point out. That should you forget to sign your application at all, which is a little bit easier to do on the Mac, we're not going to hit any server. So if you're just getting back errors all the time, make sure your application is signed.

The question comes up quite often, how can I know for sure that I am pointed to the sandbox? Well, the easiest way to do that is to look in any dialogue that comes up, and it will say environment sandbox. So that obviously doesn't happen on the production store, but this is your clue that you are successfully pointing to sandbox. A few notes about the sandbox environment and how to get set up.

You're going to want to first do a little bit of setup in iTunes Connect. You're going to want to create a test user. You obviously have a user for the real app store. You need a separate one for the test sandbox environment. And I do want to point out that you can use the same users across iOS and Mac. You only need one for both.

You'll also want to enter information about the products that are for sale. So the server needs to know about them. That's the setup step. From there, you want to build and sign your application, of course. And on the Mac, there's one additional step, which is fetching a receipt.

All Mac apps have a receipt inside of them. Not going to go into detail at this moment, but I will point you to information on how to do that a little bit later. So that's everything you need to know to write the code in your application and test it.

I want to focus now on that purchase queue again because it's really important. So let's first survey all the different players that are involved with the purchase. There's your app, of course, which links against the Store Kit framework. has been the first to launch a new app called iTunes Store, which has the SK Payment Queue. Now, you've done as we recommended, hopefully, and you've registered in an observer on the launch of your application.

And the last player involved is the iTunes Store, which lives someplace across the cloud. So the user has chosen to buy something. You've created an SK payment object. And of course, you've added it to the queue to start the process. What Store Kit will do is wrap that payment in an SK transaction so that we can keep track of some state between Store Kit and the server. And we'll hand it over to the server. The server will charge the credit card, actually complete the purchase, and we'll hand it back to Store Kit when it's successful.

At that point, we'd like to hand it back to your application, but this is where things get really interesting. Your application might not be there. So the user could be on an iPhone, and they were on a train and literally just entered a tunnel. Or it could be as simple as they just quit your application.

I would say maybe it crashes, but of course, that doesn't happen to our applications, right? Daniel Feldman Now, how do we know when to give it back to you? Well, that is why it's so important that when your application does come back, you register that observer, because there could be something still in the queue, and that's when we know to hand it back to you.

Daniel Feldman So the takeaways from here are, first, register that observer on launch, because something could come out of the queue at any time.

[Transcript missing]

So now that we've focused on that, I want to tell you about a great new feature that we have. Starting with iOS 6 and Mountain Lion. We previously said you have two choices for making your asset available.

[Transcript missing]

We think this is really exciting, especially for small developers. Why is it so exciting? Well, the main reason is you no longer need to host this content yourself. This is going to save you tons of time and money and bugs. You can also depend on the scalability and reliability of the app store itself. So you write some really cool app. It gets successful overnight.

Great. You don't have to drive off to North Carolina and set up your own data center. We've actually already done that for you. So you can just rely on us for that. It's also going to save a lot of time on the client side with our easy-to-use API that we're about to go into. Again, saving you development time.

[Transcript missing]

And finally, you can take advantage of background downloads both on iOS and the Mac. So what that means is that if the user quits your application, we'll keep downloading it for you pursuant to certain battery-related cases. A few more notes about this hosted content. There is no additional cost to host your content with us.

As you know, it's a 30/70 revenue split, and that continues to be the case even if you host your content with Apple. There is a 2GB limit per product. And then there's a few things I want to point out that are not different from In-App Purchas before but are worth mentioning. They do go through review, so they need to be approved before they go live to users.

They can't contain code, nothing that's executable, and whatever's inside them has to follow the same rules for content that applications do themselves. There's not a backdoor to sneak new content into your apps. So, knowing that, here's the overall process for how this works. It's pretty simple. You're going to build and test your content just like before, write an issue to your magazine, make those recipes, levels to a game. Now, instead of just putting metadata in iTunes Connect, you're going to actually upload that asset to iTunes Connect.

So let's actually go through the same steps we did for Purchas and just look where there are changes now that when you have content that's hosted with the App Store. First step, getting your identifiers. No change there. Get them from your bundle or get them from your server.

Second step, getting that information. Well, setting up the SK products request object is the same, but there are a few new fields on the SK product objects that you get back. The first one is a Bool. It's downloadable. And that will be yes if that product is hosted with the App Store, or no if it's not. If it is, you can get a content version. This is something you will set, something like 1.0.1, whatever the case may be, just like an app version. And you can get a content length so you know how big ahead of time that download is going to be.

Daniel Feldman You'll notice it's an array. This is a future-proofed API, so you'll notice that we're plural in a few cases, but you can expect one content length here for now. Next step, showing the UI. Same as before, it's entirely up to you what you want to do inside of your application.

Making the purchase? Same as before. Create an escape payment, add it to that queue. Processing the transaction is where things start to get a little bit interesting. Same callback, loop through it, but now in iOS 6.0 and Mountain Lion, each transaction object has an array of downloads. If that's not nil, it means that there's a download that's hosted with the App Store. So you can check that to know if you have something hosted. To start the downloads, it's as simple as calling a new method on the purchase queue, Start Downloads, and we'll kick it off.

So before we said making your asset available was the step where you would download something from your server. Now it's the step where you're going to download something from our server. We have a new callback method on the queue, updated downloads, that we're going to call over and over again as the download proceeds, and we'll continue to give you download progress about the state of the download.

Each download in that array is a new class, skdownload, and I want to show you a few of the properties that are available to you on it. Notably, there's progress. It's going to be a number between 0 and 1. 1 means 100% of your download is complete. We'll give you a time estimate in seconds so that if you'd like to include that in your UI and give the user some idea of when it's going to be done, you can. Then there's state.

As the download goes along, it goes through different states. It could be active, meaning we're downloading this thing right now. It could be waiting, meaning we're downloading something else and this one is waiting. It could be finished. That means we're done, of course. And I do want to say on that that it's better to know -- to check for the state being finished to know when a download is done as opposed to checking that the progress is won. So keep that in mind. A download could be failed or it could be paused or canceled if that's what the user did to it.

If it is failed, there'll be an error. And if it's finished, there'll be a content URL property. This is a URL to the location on disk where we just put this content. So knowing that about SK Download, let's go back and talk about showing progress. Well, you'll want to respond in your observer to that updated downloads method and pull out the progress and the time remaining to update whatever UI you have in your app.

You could also choose-- you should look at the state, and if it's failed, handle that error accordingly in your UI. Now, if you want to give the user the option of pausing and resuming, you can do so with-- or canceling-- with three new methods on the payment queue. They all take an array of downloads, just like start downloads did, and you just hand them, and Store Kit will do whatever that action is.

We talked about accessing the content, when the download is finished, and only when it's finished. That content URL property will be populated and you can use it to know where on disk that stuff is. Final step, finish the transaction. Same as before, no change there. Always call Finish Transaction.

How about a restore? How does it change a restore? Well, it's really almost identical. The one thing I want to point out is that you should continue to check for transaction.downloads to know if this is something that's hosted with the store. And the difference with this restore is that you should choose-- it's your decision whether you actually want to do the download or not.

With a purchase, the user just bought one thing, they probably want it right away. With a restore, they're getting everything they ever purchased and they might not want all of that content to be downloaded at once. So depending on what they did in your app, it might make sense to show them what they bought and not necessarily download all of that content, which they could do on a subsequent restore. So keep that in mind. But regardless of whether you actually do the download or not, always call finish transaction.

And so those are all of the code changes that it will take to use App Store hosted content inside of your application. But what actually are these things? I want to talk about the content, what it is on disk, and how you're going to build it. Well, it's basically a folder. It's a folder with whatever data you want to put inside of it that makes sense for your application.

Just a couple of things that we need-- a content Info.plist right at the root level of it with two keys, a content version provided by you, something like 1.0, and an IAP product identifier. This is the unique identifier that should match whatever unique identifier you put in iTunes Connect.

The other thing that you need is inside of the main folder, there should be a subfolder that's called Contents, and inside of there is where you can put really whatever data you want. So that's what it is. Looking at it another way, if the slash is that folder, you have ContentInfo.plist, a Contents folder, and then I have a couple movies here as examples, but you would put whatever data you need. And that's what it is. How do you make it? Well, we have updated Xcode and Application Loader to make this super easy for you. So when you add a new target to your project now, you'll see choices to create in-app content for both iOS and the Mac.

Choose that, and you'll be given the chance to enter some information about it. We're going to talk in a minute about an application that lets you buy fruit, so here we're selling a banana. And this is your chance to enter that unique identifier. Do that, and Xcode will set up everything for you.

A lot going on here. I want to point out two things. First, here's where Xcode has added the folder and file for me. It's added ContentInfo.plist for me. I added that banana picture to represent whatever data it is that you would want to add for this product. And when I look at my target, Xcode gives me an easy way to enter information for it, a version and the unique identifier again.

So once you build a great level to your game, you're issued to a magazine, and you've tested it in Xcode, you're ready to upload it to iTunes Connect. Xcode helps you there too. You can archive one of these targets just like you do your application, and you can distribute it using Xcode. So Xcode can upload it for you to iTunes Connect. Alternatively, you can use Application Loader to both enter in-app purchases and upload this content. So the tools are there to support you. Thank you.

I want to now come back to where these things live on disk because this is one of the few differences between iOS and the Mac. On iOS, we're going to put it in caches to start, so it won't be purgeable while we're downloading it, but like other things in caches, after you call finish transaction, it will be purgeable.

If that's not what you want, then you'll want to move that content to the Documents folder or someplace that makes sense. It'll stay local then. It won't be purgeable. If you then want it to be backed up, you'll need to set a flag on the content so that it will be backed up either to iCloud or iTunes, depending on the user's settings. So in iOS, as soon as that download is done, it's your responsibility to move the content if you want to move it and to track where it is henceforth. A little different on the Mac.

On the Mac, we put it in a special application support folder, and we give you APIs to access it. Two APIs. These are both class methods on SK Download. The first one takes one of those unique product identifiers and gives you the URL to the content. And should you want to clean up and make some space, the second one lets you delete it.

So I want to point out that these are really for subsequent launches of your application. When the user actually did the purchase, then we had that download that content URL. And the third one is the URL that you can use to go get the location of the content. So if you relaunch your app, you don't have that, you need these APIs to go and get that content.

Of course, on the Mac, we recommend checking your receipt, getting the product identifiers from there, so you'll have them to go and look up this content. And that's where it actually gets installed. Now, there might be a time when you want to update content. How can you do that? So you do have a recipe app. You're selling for a dollar grandma's famous chicken noodle soup recipe, and you forgot the main ingredient. Grandma ain't gonna be happy. How do we update this content when it's already out on users' devices? Well, naturally, first you need to edit the content.

So if you have a level to a game or a recipe, you need to change whatever files, whatever content needs to be changed to make that happen. You'll want to bump that version in content Info.plist. So if it was 1.0, maybe it's 1.0.1 now. And re-upload it to iTunes Connect.

Nothing automatic happens from there. For the content to get to a user's machine, they need to initiate a restore. So if you should choose to prompt the user that there's something new, this is how you can do it. You'll first want to get the SK products, just like we did before when we were making a purchase so you have all the up-to-date information. And you want to compare the version there with the version in your contentinfo.plist. So essentially, check the version of what's on disk with the version that's on the server. And if what's on the server is newer, you know that there's a new version of your content available.

One note for those of you who already use In-App Purchas and would like to transition to hosting your content with the App Store, and that's that these products must be listed as new products in iTunes Connect. There's no magical bit that can be flipped to take something existing and start hosting it with the store. So you do need to support any products that you're already selling.

Daniel Feldman So with that, I'd like to take you through how this actually works. We are going to be looking at an app called Fruit Stand. And Fruit Stand is a silly little thing that lets you buy pictures of fruit. And we are going to take it from where it was last year, where you could buy pictures that were already inside of the bundle of the application. We just unlocked them to using App Store hosted content. So let's go ahead and do that. Daniel Feldman So here's Fruit Stand. Let me show you around.

First thing we're doing up here is just logging what's going on so that it's easy to see. You notice we already added an observer onto the queue, and we've loaded product identifiers in from the bundle. So in our case, we just have them in the bundle. Not talking to a server, very simple. We have a button here that will start fetching that product info. When we get it, the idea is that we will populate a list of things for sale. You can select one and purchase it. And if all goes well, the pictures of the fruit should appear here.

We also have a downloads window where, by the end of this, we'll hook it up so that we can see progress of in-app content that's being downloaded. So now that we've seen it, let's go and look at how we're doing some of this in code. Let me make Xcode a little bit bigger. So I want to start an application that did finish launching. You notice that the very first thing we're doing is adding that observer, in this case, self. We get our product identifiers from the bundle. We'll do some things that are Fruit Stand specific.

And here we're showing content that was previously purchased. So we haven't bought anything yet, but if we had, this would be where we go and get the pictures of fruit already and load them into our app. Here's the IB action that responds to that Fetch Product Info button.

And you can see in here, we're taking those identifiers that we got from our bundle, and we're creating an NS set so that they're unique. And we're creating our SK Products Request object. Set yourself as the delegate and call start, and Store Kit will go and talk to the server.

Here are those two callback methods that we talked about earlier. If there's an error, you'll want to handle it in some way that's appropriate for your UI. But if it's successful, you'll have a response object with an array of products that you'll want to keep around for later. You'll also want to update your UI so that the user knows what they can buy. So let's look at that.

So we click Fetch Product Info. Here you can see all the things that are for sale now. So you can see that we took the title and the price out of the SK products that we get back from the server and we're putting them here. But I want to point out the additional information that you have available to you now that you're using hosted content. So let's go back into the code and make a change.

So let's look inside of where we're actually updating that table. So this is specific to our demo app, but I want to point a couple of things out. You can see we're iterating over the products, setting up our price, and creating that string, which is what you see.

But what we can do now, should you choose to, is you can check product.downloadable to know if this is something that's hosted with the store. So let's go ahead and put the old string in here. And although this wasn't running-- Let's see if that took effect. In fact, that's not the version that we're running. Let's just make sure that we have an executable.

Well, this is a good chance to show you fetching a receipt. So the first time you want to fetch a receipt, you'll need to run it outside of Xcode. Once you do that, you'll be prompted to log into the store, assuming you exit with the right exit code.

And once we have a receipt, your app will relaunch. So we have a receipt inside of our application now. We can go back to Xcode and run it. And this time, Fruit Stand will run for real. Fresh product information now, and you can see that change that we made taking effect.

So we only have the lemon that's actually inside the bundle now. The other pieces of fruit are hosted with the store. So you wouldn't really want to do this inside your application and show that they're hosted, but should you want to know which ones are hosted, you have that Boolean to take advantage of on your SK product. So now you should be able to select something and buy it, but we have a little work to do yet before that's done.

So here's the IB action that's called when you click on that purchase button. We're going to figure out from that table which piece of fruit the user was intending to buy and get the SK product associated with it. Create an SK payment with that product, and we're going to add it to the queue. And that's going to start the purchase process.

So here's that infamous callback updated transactions. You can see that we're looping through the transactions, and we're switching on the state and handling each state. But what I want to show you is just the purchase state for now. We're going to look inside Providing Content for Transaction. And here you can see the way that we were doing things up until now, which is just getting fruit from our bundle. So we're getting an image actually inside of our bundle. We're showing it in the UI. And of course, we're calling Finish Transaction. But we're going to need to make some changes here, so let's do that.

Now we actually want to start the download. We can check transaction.downloads to know that this is hosted. This is specific to our demo app, but we're going to show a progress bar. You would do whatever UI you want to start. And you'll just call startDownloads. And the downloads will go ahead and begin.

Now if there's no downloads, we'll want to do what we were doing before because we know that it's one of our older in-app purchase items. So in order to get progress, we know that there's a new callback that our observer needs to implement, and that's updated downloads. So what's going to go in there? Well, I'm going to drop in a bunch of code, and then we're going to talk through it.

We're going to loop through those downloads. Again, this is a future-proof API. Right now, you really just have to expect one download at a time. We're going to log so that we know what's going on. We're going to switch on the state to do different things. But really, we're just updating UI. So if we're waiting, paused, or active, we're just going to update that progress bar by pulling out the progress from that download and the time remaining so we can go and update our UI.

If the download is canceled or failed, well, it's over, essentially. So in addition to removing the progress bar, we also want to call Finish Transaction. If the download is finished, well, we're ready to show our fruit. So we do that. We also want to update our progress bar in our download UI, and finally, call finish transaction because we're done.

So I want to jump inside and show you how we're showing that piece of fruit. So because we know the download is in the finish state, content URL is populated. So we're ready to use that URL to actually show the fruit. So that's what we're doing here. Having done that, let's try and do a purchase. So let me bring up the download window so you can see that.

We'll fetch product info. This time we'll choose a piece of fruit and we'll click purchase. You can see we're prompted to make sure this is really what we want to buy. We are in the sandbox and it's asking me to log in. I've already actually bought a pair before, so it's just telling me that that is the case.

And when that happens, you can see it's pretty fast, but the download just completed, and there's our pair. Let's do one more of those.

[Transcript missing]

So that's great. But if I launch the application a second time, you'll notice that the fruit didn't show up. Well, we don't want that.

If somebody bought something inside your application and they relaunch it, you want them to get that content. So we're going to have to make a change there. So I'm going to jump back to Did Finish Launching, and it's time to look at this method, Show Previously Purchased Content. You can see what we're doing right now only works with something that's in the bundle already.

So we've created this array of identifiers from the receipt. Because Fruit Stand is a Mac app, we've gone and read the receipt. We've verified it was legitimately purchased. And we've pulled out the identifiers for the things that were bought. The receipt on a Mac contains a record of every single in-app purchase that was bought. So we have those identifiers. And what we're doing now is loading fruit from our bundle. But that's not what we want to do with hosted content.

What we want to do with hosted content is use that identifier and the new class method on skdownload to get the URL to that content. If there is a URL, we know this was in fact hosted. And we're setting some state here that's specific to our demo app. You would do whatever you need to do. And we're showing that fruit. So we can move this old way of doing things in the else statement.

And now we should be loading content into our app on launch, whether it's from the bundle, something we purchased, you know, an old product, or whether it's one that's hosted with a store. So if we relaunch our application, you can see there's our fruit. Everything is working great.

So we took a look at Fruit Stand, silly application, but that's what you would need to do if you want to go from an application that already uses in-app purchase and just gets things out of your bundle or your own server to using the new app store hosted content. I'd like to wrap up with a few best practices.

The first one is just remember always to check that queue on launch. Transactions can come out of the queue at any time, and you should be ready for them. I do want to point out one more thing about finished transaction. If you call it in the middle of a download that's coming from the App Store, it's the same as a cancel. We will cancel the download. So you want the download to complete before you call finished transaction.

Third, restoring purchases is required. Your users need a way to get back all of the content that they've purchased. On iOS, make sure that you move your content out of caches if you don't want it to be purgeable. Maybe you do, depending on your situation, but make sure you move it if you don't.

Always test in Sandbox before deploying. Can't say that enough. That's the only really legitimate way to make sure that everything is working. So in conclusion, I just want to say thank you. In-App Purchas is a huge success, and it's because of you guys. If you haven't used it, consider using it and what it might do for your application. If you have, consider hosting with the store. We think it'll really make things easier for you. And if you've used In-App Purchas on one platform, consider using it on the other.

We have some great information to help you with In-App Purchas. There's, of course, our lovely evangelist. There's also some really good documentation. There's the In-App Purchas Programming Guide. It talks about everything I talked about and in much more detail. We also have a document on validating App Store receipts for both iOS and the Mac. It teaches you everything you need to know. And with that, let's finish this transaction. Thank you very much.