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: wwdc2020-10661
$eventId
ID of event: wwdc2020
$eventContentId
ID of session without event part: 10661
$eventShortId
Shortened ID of event: wwdc20
$year
Year of session: 2020
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2020] [Session 10661] What’s ne...

WWDC20 • Session 10661

What’s new with in-app purchase

App Store and Distribution • iOS, macOS, tvOS, watchOS • 45:53

Create a great in-app purchase experience for your iPhone, iPad, Mac, and Apple Watch apps. Discover how to handle refunds, integrate new App Store server notifications, and find out how to use receipts and server notifications to manage subscriber status. We’ll also walk you through the latest updates in StoreKit, including in-app purchases on Apple Watch, Family Sharing, SKOverlay, SKAdNetwork, and more.

Speakers: Tori Shurman, Ross LeBeau

Open in Apple Developer site

Transcript

Hello and welcome to WWDC. Hello and welcome to WWDC. I'm Tori, and I'll also be presenting with my colleague, Ross, who you'll hear from later today. I'm so excited to share with you what we have new for you with in-app purchases. And we have a lot to cover today. This session will be divided into two sections. I'll focus on covering what's new on the server side, and later I'll hand it off to Ross to cover StoreKit updates.

So let's get started with our server updates. On the server side, first, we'll cover refunds and how you can handle transactions that are refunded. Next, we'll cover some new ways to help you manage the subscription status for your customers and how you can use App Store Server Notifications to get notified and respond to various subscription billing events.

We'll also cover the different scenarios where you may still need to use verifyReceipt to get the latest status. And finally, we'll dive into Family Sharing. We have a lot to cover, so let's get dive right in with arguably what brought us all here today: an in-app purchase. So this is an in-app purchase in one of my apps.

I'm so excited to use this, but what happens if I change my mind? If I have an issue with the content and call Apple to request a refund, what should you, as a developer, do? That's why handling refunds is so important, but keep in mind that while it's important, it only affects a small percentage of transactions.

However, proper refund management could drive that percentage down. So let's dive into that now by walking through a typical refund scenario. First a customer purchases some content in an app, like 100 gems. However, the purchase was an accident, so they call Apple for support. After considering their case, we issue a refund.

Later, the customer contacts you for support as they notice they still have the gems that were refunded. If you don't know that Apple has refunded the content, it's difficult to determine how to respond. It would be so much better if you could easily determine if we had already refunded that purchase so you could take action appropriately, such as acknowledging the refund but letting the customer keep their gems, or deducting their balance. That's why we're working to bring you new ways to manage refunds for your content.

Having the ability to manage your refunded purchases is important for many reasons. Most importantly it gives you control to take action as you see fit, such as messaging the customer, or even taking back the content if needed. It also lets you handle any potential abuse of your content, such as customers trying to keep their content after receiving a refund from Apple, and you can resolve customer issues like the previous one swiftly.

This will also allow you to manage your in-game economy, making gameplay more fair for all players as there will be repercussions for refunds. So for all of these reasons, and because we want you to be able to manage refunds for purchases in your app, we're bringing you a brand-new App Store Server Notification and our first ever notification for content types other than auto-renewable subscriptions, the refund notification.

So why did we decide on an App Store Server Notification for this? Well, the primary reason is that you don't have to ask us for information. We'll just tell you. You're notified immediately with a JSON POST upon a status change, and we even retry up to three times if we don't get an HTTP OK back from you.

If you're already receiving App Store Server Notifications for auto-renewing subscriptions, then you'll get the new refund notification for your other content types with little additional work on your end. We'll also send you an updated unified receipt with your canceled transactions included so you can update your records. And this solution is also scalable as you grow your business on our platform.

So our goal for all content types is to allow you to obtain information about refunded purchases through App Store Server Notifications. For consumables, non-consumables and non-renewing subscriptions, you'll receive the brand-new refund notification. For subscriptions, you'll continue to receive the cancel notification. Enabling App Store Server Notifications is straightforward if you've never done it before and can be done in a few steps. First, set up your desired endpoint for your notifications in App Store Connect. Next, make sure your endpoint meets App Transport Security requirements as outlined in the developer documentation. Then you're all set to start receiving notifications.

So in App Store Connect, just navigate to your app's page and find the URL for App Store Server Notifications section. Enter your desired endpoint here so we know where to send your notifications. Know that if your endpoint already meets security requirements, you'll immediately start receiving notifications. Now let's take a deeper look at the refund notification. We'll send you this notification when any consumable, non-consumable or non-renewing subscription is refunded for your app after we issue a refund to the customer for that purchase.

Getting notified in this way makes it easy for you to take immediate action on your refunded content. This notification has also been implemented in a privacy-friendly way, as we're not giving you any information about the customer, only information you'd already have about the purchase. The refund notification is live today, so if you're already receiving App Store Server Notifications, make sure you're looking for it.

With that in mind, when you receive this notification, there are a few things I want you to look out for in the payload. You should look for the original_transaction_id to tell you which transaction we've refunded, the cancellation_date to know when we refunded it, and the cancellation_reason. The reason can have values of 0 or 1, and a value of 1 can indicate the customer requested a refund due to an issue within the app, which you could then investigate.

Also look for the bid and the product_id to verify the app and product you've received a notification for. All these fields can be found in the unified_receipt object in the App Store Server Notification payload in the latest_receipt_info section, except for the bid, which is found at the top level of the payload.

So what does the App Store Server Notification look like? Well, it looks something like this. Not all the fields are listed here, but we have explanations for all possible fields in the developer documentation. Right now let's take a look at the ones we just discussed, plus a few others. In addition to those transaction identifying fields, look for the password in the payload. This is a shared secret for your app, which you can find in App Store Connect, and it allows you to verify the payload is from Apple and is trustworthy.

At the same level as the password, you'll find the bundle identifier. You can verify this field to know which app you've received a refund for. Next, look in the unified_receipt object, specifically in the latest_receipt_info array, for information about your refunded transactions. This array contains the 100 latest transactions for your app and the four fields we told you to look for: the cancellation_date, the cancellation_reason, the original_transaction_id and the product_id.

So let's revisit our refund scenario from earlier and look at how the refund notification can now help you in a slightly different situation. In this situation, the customer still buys 100 gems, but then consumes the gems and still asks Apple for support with their purchase. Once again, we make a decision to honor or deny the refund.

Because we don't know if the customer has consumed the gems, we may still honor the refund, and if we do, we'll send you a refund notification. Then, if the customer reaches out to you asking for further support, such as in-game compensation, but now you'll know the purchase has been refunded and you can choose to take proactive action, such as providing an in-app message in your app.

A message such as this is great as it notifies the customer that you've observed a refund for their purchase, the action you've taken, and what they can do to regain access to the content. In-app messaging is just one of many actions you can take upon observing a refund. Let's take a quick look at those now.

So there are many actions you can take depending on the content type, ranging from moderate to severe. For all content types, you can use this for refund monitoring, for in-app messaging and restricting access to the refunded purchase. Because we're sending you a server-to-server notification, this gives you the ability to restrict access cross-platform if needed.

In the case of consumables, there are additional actions you can take, like deducting the in-app currency balance. You, as a developer, are responsible for making decisions on what measures to take and how to implement them. So think carefully about which action you decide to take in order to promote a healthy community within your app. So now we've covered a lot about refunds and how to handle them appropriately. So now let's switch gears and jump into how we are making it easier for you to manage subscriptions, first taking a look at some of the key events in the subscription life cycle.

These include acquiring a subscriber for the first time, any auto-renew, successful or unsuccessful, disabling or enabling auto-renew, upgrades or downgrades and cancellations. So what do all these events look like for a customer and for you? So when the customer first subscribes, we establish the subscription period and a fixed auto-renew period to repeat renewal after renewal. Within the first subscription period, your customer decides to turn off auto-renew, thinking that they just aren't using the subscription enough, but later turns it back on after a little more time with their subscription.

After that, we approach our first scheduled auto-renew, but there's an auto-renew failure due to a billing issue. At this point, we'll try to auto-renew for the duration of the billing retry period, also establishing a grace period. Luckily the subscription is recovered during our grace period, so the renewal cycle remains the same.

Shortly thereafter, your customer decides they're enjoying the subscription so much that they want to upgrade. For an upgrade, we start the customer at the higher tier immediately, shifting our upcoming renewals. Later, after not using the higher tier as much as expected, your customer decides to downgrade back to a more basic tier. Downgrades take place at the end of the subscription period, so your customer will have access to the higher tier of service until their next scheduled auto-renew.

So how can you keep track of all of these important events for your subscriptions? What we recommend is using App Store Server Notifications for status updates. This is a push approach, meaning you don't ask us for information, we'll just tell you when something happens, and we do. We notify you when the status of one of your subscriptions changes, providing you with a new receipt for your records at that time, but only when you need it. This solution is also more scalable as you acquire more subscribers. So let's dive into our App Store Server Notifications now to see what we offer and to learn a little more about them.

When you start receiving App Store Server Notifications, or if you are already are, this is the payload you can expect to see. Note that this a subset of possible fields and not all of these may be present all the time. There's a lot of information here, so I want to focus on some of the key components. When you receive this payload, first look for the auto_renew_product_id to see exactly which product the notification applies to and the product we plan to auto-renew next. Then check the notification_type. This will tell you the type of event you are receiving.

You should also verify the value in the password field matches your shared secret, so you know the content comes directly from Apple and is trustworthy. You should also look for the bundle identifier. This will tell you which of your apps you've received a notification for. Then you want to look for the unified_receipt. This object mimics the response from verifyReceipt, so if you're already using verifyReceipt, this should be familiar.

The latest_receipt in the unified_receipt object gives you an updated app receipt for this customer for your app. The latest_receipt_info array contains the 100 latest transactions for your app. You can search here by the original_transaction id to know which subscriptions are active for this customer. Finally look at the pending_renewal_info array to find the upcoming renewal info for each subscription the customer uses in your app. Now let's quickly revisit the notification_type field.

We'll send you notifications for several events, and the notification_type field can have the following possible values: INITIAL_BUY, when a customer first subscribes, INTERACTIVE_RENEWAL, when a customer upgrades or renews their subscription in your app or through Manage Subscriptions... DID_CHANGE_RENEWAL_STATUS, when a customer changes their renewal status, such as toggling auto-renew on or off, DID_CHANGE_RENEWAL_PREF, when a customer makes a change that takes place at the end of the subscription period, such as a downgrade, CANCEL, when a customer calls Apple Support, and we issue a refund to them for their subscription or for an upgrade as we cancel the previous lower-tier subscription, DID_FAIL_TO_RENEW, when we fail to auto-renew as scheduled due to a billing issue, and DID_RECOVER, when we recover billing of the subscription in the billing retry period or grace period.

For more information on these notifications and when you can expect to observe them, see our WWDC 2019 session, "In-App Purchases and Using Server-to-Server Notifications." To learn more about which notifications are available to you in SandBox, check out this year's session "Introducing StoreKit Testing in Xcode." So if we go back to our timeline from earlier, we have notifications to cover almost all of these events. INITIAL_BUY, for the purchase of the subscription, DID_CHANGE_RENEWAL_STATUS for disabling and enabling auto-renew, DID_FAIL_TO_RENEW for the renewal failure, DID_RECOVER for the billing recovery, INTERACTIVE_RENEWAL for the upgrade, plus a CANCEL for the previous lower-tier subscription, and DID_CHANGE_RENEWAL_PREF for the downgrade.

So with App Store Server Notifications, you can easily monitor your subscription events. But we decided we could make this just a little bit better, which is why this year we're bringing you an App Store Server Notification for every successful auto-renew. This is a new notification type, which we're calling "DID_RENEW." Coming later this year, we'll send you this notification after every single successful auto-renew, every time.

Like all the other notifications, it contains the unified receipt, which gives you all the information you need to identify the subscription that was renewed. This includes the original_transaction_id, which is a unique identifier for the subscription, the transaction_id, a unique identifier for the new subscription period, the expiration date of the new period, and the auto_renew_product_id to tell you exactly which product was auto-renewed.

So jumping back to our timeline, notice that the addition of the DID_RENEW notification completes our events, bringing you everything you need to monitor your subscriptions in App Store Server Notifications. However, in addition to this new notification, we thought there was a little more information you might need concerning your subscriptions.

We think you should know if one of your customers has decided to apply a subscription offer before we renew the subscription with that offer. That's why we've added the promotional_offer_id to the pending_renewal_info section. This will be available through verifyReceipt and in the App Store Server Notification in the pending renewal info array, so you'll know if there are any pending offers for that subscription.

This feature is live in the pending_renewal_info section today, so be sure you're looking for it. For more information on using subscription offers, see our WWDC 2019 session, "Subscription Offers Best Practices." So now that we have a DID_RENEW notification and the promotional_offer_id in the pending_renewal_info, what should you do with this new information? Most importantly this allows you to rely on App Store Server Notifications for updates, eliminating the need to poll verifyReceipt.

However, you should still use verifyReceipt for a few key use cases. If you experience an outage on your server, the next time one of your customers comes online you can call verifyReceipt to check their status, making up for any missed updates. In this way, verifyReceipt offers a recovery mode.

Second, you can use verifyReceipt to immediately determine entitlements for a customer. To learn more about determining entitlements, tune into this year's "Architecting for Subscriptions" session. Finally, you can use verifyReceipt to verify a successful auto-renew. So what do we mean by using verifyReceipt to verify a successful auto-renew? Because auto-renew is so critical, you want to ensure that you get the update. For this reason, approach marking an auto-renew through a double check.

First, subscribe to our App Store Server Notifications so you receive the DID_RENEW notification in addition to our other notification types. Second, schedule a call to verifyReceipt for your subscription shortly after its scheduled expiration date. When you receive the DID_RENEW notification as expected, you can cancel this job, but if there is a delay, you can fallback to verifyReceipt to verify a successful auto-renew. So we just covered a lot about App Store Server Notifications and subscriptions. Now I'd like to discuss a new feature for App Store subscriptions. One of the things customers love most about subscriptions like News+ or Apple Arcade is that they can be shared with their family members.

Well, we're very excited to announce that this year the App Store will support Family Sharing for in-app purchases, which will allow up to five additional family members to share a single purchase. Family Sharing works for both auto-renewable subscriptions and non-consumable in-app purchases like those that are used to offer a full-feature unlock.

Customers will choose which in-app purchases they'd like to share with their family. We're so excited about what this means for developers and your ability to engage millions of family members that are already set up. Because the use of family is so widely adopted, Family Sharing for in-app purchases can help increase customer engagement and improve retention for your app. To begin, you can visit App Store Connect to turn on Family Sharing for a specific product. You can navigate to your app's page, and select "Turn On" in the Family Sharing section. Note that once you turn on Family Sharing for a product, you cannot turn it off.

Let's review how customers will manage their shared purchases. When new customers subscribe, the subscription will be shared by default. Subscribers can turn off sharing later for their family on the Manage Subscriptions page. For existing purchasers, they'll be notified if one of their subscriptions becomes shareable. Then, they can manage their sharing preference from the same Manage Subscriptions page.

For non-consumables, all purchases will be shared with the family if the customer has turned on "Share Purchases with Family" in Settings. So what do you need to do in your app so family members can enjoy access to the shared purchase? With this feature, not only are we creating a transaction for the purchaser, we're also creating a transaction for each family member. So when a family member opens the app, they will have a transaction available in the transaction queue, for each of their devices. And there will be a unique receipt for each family member that will contain shared transactions in the latest receipt info array.

You should keep track of the original_ transaction_id for each family member as you already would for your subscriptions. Note that this only applies to new purchases made after sharing is enabled. For non-consumable purchases made before sharing was enabled, you'll still have to restore purchases for these to be available to all family members.

So let's get an idea of what this looks like for new purchasers. After you've enabled Family Sharing in App Store Connect, when a family member makes a shareable purchase, not only do we put a transaction in the transaction queue for their devices, we also put a transaction in the transaction queue for every member of their family for all of their devices. So when their family members open up your app later, you can unlock access to the subscription or non-consumable instantly. It's like magic, and it makes it so easy for you to reach more developers than ever before.

Before moving on, let's quickly review what this looks like for both new and existing subscribers for both subscriptions and non-consumables. So, for new purchasers, any shareable subscription they purchase is shared with their family by default. Any sharable non-consumable they purchase is shared with their family, if they have purchase sharing enabled in iCloud if the family is sharing payment, and if the app is not hidden from their purchase history. For existing purchasers, for a sharable subscription to be shared with their family, they must opt-in from the managed subscriptions page. For existing non-consumables to be shared, the same three conditions must be met, and you have to restore purchases to unlock the content for the family member.

To manage access for all family members, you'll need to use the receipt. You can get an updated receipt from verifyReceipt or from App Store Server Notifications. Additionally, whenever the customer opens the app, you can refresh the receipt on device to verify access for each customer. But I'm getting ahead of myself here. So with that, I'll hand off to my colleague Ross who will talk to you more about Family Sharing within StoreKit.

Thanks, Tori. So let's take a look at how Family Sharing and App Purchases works within your app. First off, how can your app tell if a product supports Family Sharing? Well, we've added a handy new property on SKProduct called isFamilyShareable. It's as easy to use as requesting a product information from the app store, and then checking the isFamilyShareable Boolean. You can use this to programmatically display to customers that one of your products is Family Sharable, and not have to hard code any logic.

So now one of your customers has seen this Family Sharable product, and taps to purchase it. What happens next? Well, when a customer purchases a Family Sharable in-app purchase, everything begins as normal. Your app uses StoreKit to send the purchase to the app store, and then you get a transaction back in the purchase state. The next time their family members open your app... each of the family members will get a transaction that looks just like a restored purchase. So your existing app logic should be able to handle it with no additional coding.

Now, Tori mentioned that customers can also enable and disable sharing for specific products. When a customer enables Family Sharing for a product, something similar to a purchase happens. Their family's devices will all get a transaction that looks just like a restored purchase. So again, your app should be able to handle it with no additional coding.

But what about the opposite case? What about when a customer disables Family Sharing? Well, normally non-consumables are permanent, and auto-renewable subscriptions only end when they expire. But when a customer disables Family Sharing, they expect access to be halted immediately. So for this case, we've added a new API.

It's a new method on the SKPaymentTransactionReserver protocol called paymentQueue didRevoke EntitlementsForProductIdentifiers. When a customer disables Family Sharing, StoreKit will automatically update your receipt, and then call this method. Inside the method, you should be prepared to verify the receipt, whether locally, or by uploading it to your server and using the verifyReceipt endpoint.

Products that have been revoked will no longer be present in the receipt. Now, you should make sure that you're not simply looking at the product identifiers in the array and revoking access based on that. Depending on the set up of your products, and other products the customer may have purchased, they may still be entitled to some access that overlapped the revoked purchase.

So we added this API to handle a couple of new situations that occur due to Family Sharing. As I mentioned, it will be called when a purchaser disables Family Sharing for a product. It will also be called if a customer leaves a family group that was sharing a purchase.

And we've also taken this opportunity to add support for refunds. If a customer receives a refund for a non-consumable, or an auto-renewable subscription, StoreKit will call this method, and you may revoke access immediately. And that wraps up Family Sharing, which we think users will love, and will give you new opportunities to increase value to your customers, and improve engagement and retention.

Now, I'm going to give you a tour of some of the other improvements and features we've added to StoreKit, and what to look forward to in the latest releases. First, I'll start with a couple things we already shipped since WWDC last year. We have in-app purchase on Apple Watch, and improvements to our subscription price increase flow.

Then, I'll move on to what's new, including SKOverlay, and improvements to SKAdNetwork. So this first one is especially for all you Apple Watch developers out there. As of watchOS 6.2 earlier this year, we've added StoreKit and in-app purchases to watchOS. This means that for apps your customers use mostly on the watch, you can offer in-app purchases directly in the watchOS interface, your customers are already using.

Using StoreKit on watchOS is almost exactly the same as using it on any other platform. You can observe the payment queue, request products, and add them to the payment queue the same as you do in other platforms. When it comes to verifying the receipt, you can, of course, continue to use server-to-server validation.

However, if you prefer to do local validation, there's just one difference you should be aware of. When you're retrieving the deviceIdentifier, instead of UIDevice, you need to use the WKInterfaceDevice API. Here's an example of some code that runs on both iOS and watchOS. Once you've retrieved the deviceIdentifier, everything else is the same. Use that deviceIdentifier along with the opaque value in the receipt, to create a hash. Then make sure that hash matches the one in the receipt.

And that's all you need to know to add in-app purchases to your watchOS app. Another feature we rolled out in iOS 13.3 is an improvement to handling subscription price increases. A subscription price increase occurs when you have an existing subscription product you want to charge more for. Setting up a price increase is easy. You just go into app store connect and change the price. But you can't just bump up the price of an auto-renewable subscription without telling your customers. That's why the app store requires each customer to agree to the new price.

You should note that if you decrease the price of a subscription, customers don't have to take any action, and they'll get the new price automatically. Now, unfortunately, this process of informing the customer, and asking them to agree to the new price can result in churn. Churn is when customers stop using your product, and are no longer paying for it. And I know you're thinking, "Well, of course, customers don't want to pay more." But actually we've found, in a lot of cases, that's not the issue.

Customers can sometimes miss the e-mails that the app store sends... or perhaps they don't want to engage in the price increase flow at that time. And sometimes they put it off until later and simply forget. Additionally, customers might not be informed of additional value that you're providing. For example, if you run a video service, and you've added new content. So for users experiencing a price increase who are using your app, we've implemented an automatic in-app price increase consent sheet.

After you initiate a price increase in app store connect, any affected customers will see this app store UI when opening your app. This way, customers are informed, and can agree to continue their subscription at the time they're using the app, which presumably means they're finding value in your products.

Once they agree, or dismiss the sheet, your app's UI's underneath it and continues as normal. Now, we realize that not all apps will want to display the sheet immediately upon opening. So we've added some tools to allow you to control the flow. First, you'll want to implement this new SKPaymentQueue delegate method.

StoreKit will always call this method before presenting the price consent sheet. Inside the method, it's up to you to determine if you want to show a sheet now, and then return "True" if you do, and "False" if you don't. Now, if you return "False" here, you're going to want a way to show the sheet later at a time that's more opportune, perhaps after you've educated the customer about additional value you're providing. To do that, simply call this other new StoreKit API. SKPaymentQueue .showPriceConsentIfNeeded.

Now, you should only call this if you previously returned "False" to the method above. However, if you do call it, and there's no pending price increase, don't worry. StoreKit will always check to make sure there is a pending price increase for the customer before showing the sheet. And it won't show it at all if there isn't. Next, I'm happy to introduce an entirely new API in the latest iOS release. It's called SKOverlay, and it's a sleek new UI element for displaying and promoting apps. Take a look. SKOverlay presents a floating view at the bottom of your UI that displays information about an app.

It's similar to the SKStoreProductViewController class that exists in StoreKit today, except SKOverlay was designed to work seamlessly with your app's UI. Additionally, unlike SKStoreProductViewController, SKOverlay is only used to display apps. SKOverlay was also designed to work seamlessly with your app clips in order to help you transition users from your app clip to your full app.

But that's not all it's used for. You're entirely able to use it within your full apps as well. Simply enter the app ID of the app you want to display, and customers can install it directly from the overlay. If you're interested in learning more about our new app clips feature, I highly recommend checking out the "Exploring App Clips" session as well as the "Streamline Your App Clips" session, both in this year's WWDC. So now, let's dive into the SKOverlay APIs and see how you can best integrate SKOverlay into your app. Creating and presenting an SKOverlay is fairly straightforward. First, you initialize it using an SKOverlay configuration object.

We'll get back into that configuration object later, but it essentially allows you to set up the details of the overlay. Then, you call the present method, passing in the WindowScene where you want the overlay to appear. That's all you need to do to create and present an SKOverlay. Of course, we've also added lots of other tools to allow you to customize the overlay flow.

First, there's a dismiss function that allows you to manually dismiss the overlay. You'll notice that this dismiss function is a class function, and it doesn't take an overlay as an argument, but rather another UIWindowScene. That's because only one overlay can be displayed in a scene at a time. And doing it this way, allows you to remove any overlays that may be on a scene even if your current code context doesn't have access to the specific overlay object.

SKOverlay also has a delegate. As you'd expect, this delegate allows you to react to changes in the overlay's status. We'll see the details of that later. And finally, SKOverlay has a configuration object. This allows you to see the configuration that was used to set it up. Now that we've come back around to the configuration, let's take a look at those two objects. SKOverlay's actually comprised of two classes.

The first is called AppClipConfiguration. This configuration is used to transition users from your app clip to your full app. The app clip configuration can only display the full app for the current app clip the overlay is displayed in. The next class is the AppConfiguration. And this configuration can be used to display any app that-- Much of these two classes is the same, so let's start there.

Both classes have a campaign token and a provider token so that you can use SKOverlay with the app analytics. They also have functions that allow you to set and get arbitrary key values. Now, most developers won't need to use these, but they're there so that you can integrate SKOverlay with other StoreKit APIs, such as SKAdNetwork.

Both classes also have the position property. While SKOverlay always appears at the bottom of your screen, apps that use tab bars will want to choose the bottomRaised property, so that the overlay appears just above the tab bar, rather than on top of it. In addition to these properties and functions, the app configuration also offers two additional properties on top of these.

The first is the appIdentifier. You can use this to enter the iTunes identifier of the app you wish to display. The app clip configuration doesn't have this property because, as I mentioned before, that configuration is only used for displaying the full app of the current app clip the overlay is in.

And the app configuration also has a userDismissable Boolean. This is set to "True" by default, which means that the user will be able to swipe down in the overlay to dismiss it from the screen. If you set this to "False", the user will not be able to swipe down on it, and the overlay will only go away when you call the dismiss function manually.

That's it for the configuration. So let's move on to the delegate. The first delegate method is simply an error handler. If you try to present an overlay and an unexpected error occurs, it will call this delegate method, passing in the overlay, and a specific error as an argument.

The rest of the delegate methods are all revolving around animating of the overlay. You can see there's a method for the start and end of the presentation, as well as the start and end of the dismissal. You can also see that each of these methods includes an SKOverlay.TransitionContext object.

That's because these methods are used to help you coordinate your UI animations alongside the overlay. Let's walk through how you might present an overlay, and use these APIs to coordinate animations. Here, we're creating and presenting an SKOverlay inside our app clip. First, we grab the current WindowScene. And then, we create an app clip configuration using the bottom position.

We'll then initialize an overlay using that configuration object. And we'll set it to delegate. Finally, we present the overlay in that scene. And this is all you need to do to use SKOverlay in your app or app clip. If we want to animate our own UI alongside the overlay, we can do so using the delegate methods. First, we can set up any initial state for our UI elements directly inside the delegate method.

And these delegate methods are always called in the main queue, so you can manipulate UI right inside them. Then, we add any animations we want inside an animation block on the transition context. Again, simply declare any changes in animatable properties here. There's no need to use a UIView animate block since any code inside this block will be animated for you by the overlay.

Whether you're optimizing your app clip or promoting other apps inside your own app, SKOverlay's a great option for a seamless integration and a beautiful UI. Finally, I'd like to share with you some updates about our SKAdNetwork API. Introduced in iOS 11.3, SKAdNetwork allows ad networks to measure the effectiveness of their ads while still respecting customer privacy.

In the latest iOS release, we've made it even more powerful, while still not compromising the customer privacy. Let's start with an overview of SKAdNetwork. SKAdNetwork involves three stakeholders, ad networks, source apps, and advertising apps. Each stakeholder has a role in making the feature work. Ad Networks places ads within apps, and receive postbacks when the ads results in conversions. Source Apps display the ads that are sent to them by the ad networks.

And advertising apps are the ones that appear in the ads, and then submit the postback to SKAdNetwork once they're opened. Let's take a closer look at this flow from start to finish. First, the ad network places the SKAdNetwork data inside an ad for the advertising app, which we'll call app B.

Then, it displays this ad in the source app, which we can call app A. Once the user taps the ad, installs app B, and then opens it, app B should call another SKAdNetwork API in order to initialize the postback. Calling this API will set a timer... and once that timer expires, the user's device will send a postback back to the ad network's URL.

Ad networks should use Apple's public key to verify the data in the postback and make sure it's legitimate. So that first bundle of data that the ad network sends up when displaying an ad looks like this. It contains the ad network ID, which has been registered with Apple, and a campaign ID from 1 to 100, which ad networks can use to measure their campaign effectiveness.

It contains the ID of the advertising app, the one that's displayed in the ad. And has a timestamp. This timestamp should be generated at the time the ad is displayed, because timestamps that are too old will cause the ad data to be rejected. The nonce is just a random view ID, used to ensure that each ad impression is unique, and to prevent double counting.

And finally, the signature is generated using all of the other pieces of data in this bundle to ensure that only your ad network can start ads using your ad network ID. In the latest iOS release, we're requiring two new pieces of data. The first is the version, which is now 2.0. And the second is the Source App ID. This is the ID of the app that's displaying the ad.

Next, the postback API should be called by the advertising app, app B, upon the first launch of their app. This method generates a postback, which is the cryptographically signed data validating that a user installed and launched this app after seeing the ad. The first call to this API starts the postback process if the device has attribution data for the app. And subsequent calls have no effect. Starting in the latest iOS, advertising apps will be able to call the updateConversionValue API if they choose. This allows the app to add an additional six-bit value that represents some action that took place in the app.

For example, let's say you wanted to know whether a user purchased an item in your app before you count the conversion. In this case, you can call updateConversionValue and include a value that you mapped purchasing an item. Since the conversion value is chosen at runtime, we can't sign it on the app store server, so it's the only piece of the postback that isn't protected with cryptography. The value itself is an integer between zero and 63, which may represent actions such as making a purchase, signing up for a free trial, or completing a level.

Apps can call this API multiple times to update the conversion value. However, only values that are higher than the previous value will be accepted. Values that are lower than the currently stored value will simply be ignored. This means you don't have to worry about accidentally overwriting your conversion value with a lower value. Now, let's see the details of the postback that StoreKit sends to the ad network once the process is complete.

We'll send up the ad network ID and the campaign ID that were used in the ad, as well as the ID of the advertising app, app B. A transaction ID is another unique identifier you can use to make sure you aren't double counting conversions. And you can use Apple's public key to verify the signature to know that it's all legitimate.

In the latest iOS, we've added new information to the postback too. First, we've added the version. We've also added a key called "redownload," which will indicate whether this was the first time the customer purchased the app, or if they previously purchased it and are installing it again. Prior to this, SKAdNetwork only worked for the first purchase of an app. So adding redownloads will give you a lot more insight into the effectiveness of your ads.

Furthermore, in the latest iOS, we've added two new optional items in the postback. The first is the ID of the source app, so that you can know which app displayed the ad that resulted in the conversion. And the second is the conversion value that was chosen by the advertising app.

It's important to note that these last two pieces of data won't always show up in the postback. In order to preserve customer privacy, the app store servers do calculations to make sure that sharing these values won't allow the postback to be linked to the customer that generated it. So we'll share them when we can, but your server should be ready to handle the postbacks both with and without them.

So how do you get started? If you're interested in using SKAdNetwork as an ad network, you'll need to sign up with Apple so we can register your information. You should enroll in the developer program as an organization, and then fill out the form to request access to SKAdNetwork.

We'll then send you instructions on how to generate a public-private key pair, and you'll send us the URL you want postbacks to be sent to along with your public key. Remember, never send your private key. Always keep that secure. Then, you're registered and ready to begin using SKAdNetwork. If you're a source app, and you want to work with an ad network, ask them for their ad network ID, and put it into your Info.plist file. This will make sure StoreKit knows to accept the ad data when you display an ad in your app.

And finally, if you're an advertising app, and you want to measure the conversion of your ads, make sure to configure your app so that it initializes the postback when it first launches. Today we've covered lots of information on best practices and new features, in both server-to-server and customer device environments. You can now take advantage of new server-to-server notifications for refunded purchases. And we saw how you can use subscription notifications to get the latest information without needing to poll the verifyReceipt endpoint.

We introduced our brand-new way to share in-app purchases among family members, giving you a new tool to increase value and engagement with your customers. On the client side, you can now offer in-app purchases directly inside your watchOS apps. And subscription developers will benefit from our new, improved subscription price increase flow.

We got into the details of our new SKOverlay API for promoting apps, including using inside app clips. And finally, we explained how our SKAdNetwork API can help advertisers gather and use conversion data without compromising on customer privacy. We think these tools will help you continue to grow your business with in-app purchases. For more information, please join us on the forums and in the labs. Thank you.