A Peek at 3D Touch

Session 228 WWDC 2016

3D Touch adds a completely new dimension to the iOS user interface and introduces an entirely new way to interact with your iPhone. Explore the existing API and get introduced to additions in iOS 10 that allow you to push the boundaries of 3D Touch. Learn about best practices for incorporating 3D Touch into your apps, how to add support for Quick Actions, Peek, and Pop.

[ Music ]

[ Applause ]

Good afternoon and welcome to a peek at 3D Touch, I'm Tyler Fox and later I'll be joined by my colleague Peter Hajas.

We're both engineers on the UIKit Team and we're really excited to tell you all today, how you can adopt 3D Touch to take your app's user experience to the next level.

We'd like to start today by reviewing some of the fundamentals of 3D Touch and showing you how it shows up across the system.

From there, we'll talk about Home Screen Quick Actions.

Home Screen Quick Actions are one of the easiest ways that you can add 3D Touch to your apps today.

They let users jump straight from the Home screen, right to those key activities in your application.

From there, we'll talk about Peek and Pop.

Peek and Pop are a seamless way for users to preview and navigate content in your applications.

We think that most apps will benefit greatly from adding, for adding support with Peek and Pop to your content.

Finally, we're excited to tell you all about UIPreviewInteraction, this is brand new API in iOS X that allows you to make 3D Touch unique to your apps.

It's for those of you that want advanced control of the 3D Touch interaction and it lets you use the same force processing that we've honed for Peek and Pop, but bringing in your own custom user interface.

It's really great and we can't wait to tell you all about it.

So with that, let's get started, by talking about 3D Touch.

Devices that support 3D Touch have a force sensitive display that precisely measures the pressure of touches on the screen.

Now iOS has always let users directly manipulate the content on the user interface.

This has been a hallmark since the original iPhone was introduced with its multi-touch display.

But 3D Touch extends this even further, allowing users to connect even more closely to their content on the screen.

Let's take a look at some of the ways that 3D Touch shows up across the operating system, starting with the Home screen.

When you press on an app icon, we reveal the Home Screen Quick Actions.

As you can see here with Camera, these let you jump straight into some of the key activities in each app that you come to perform.

For example, taking a selfie in the camera app, using the front facing camera, or taking a regular photo with the rear facing camera, recording video or a Slow-mo video.

Now, new in iOS X, if your app provides a widget it will also show right alongside the Home Screen Quick Actions when the user 3D touches on your app icon.

And something else to note is that while you're pressing on each of this app icons, the device is playing haptic feedback to let you know when you've reached the threshold where the quick actions will spring open.

Now let's move into messages, to take a look at an example of Peek and Pop.

Here in messages, you can see I've received a photo in a conversation with a friend.

If I start to lightly apply pressure to that photo, I can peek into it to reveal a nice large preview of that content.

Then if I continue to apply pressure, I'll pop straight to that photo, which takes it full screen and lets me interact with it, just like I had tapped on it.

Peek and Pop is really nice because it allows you to preview content and perhaps change your mind and release your touch without actually having to go tap the back button and navigate back.

Now, one thing to note here is that, as we're crossing these two different thresholds for Peek and Pop, the device, again, is playing haptic feedback, to let you know when you've reached these two thresholds.

That's Peek and Pop.

Let's take a look now at Mail, at some of the interesting pieces of the same interaction.

I want you to notice how fully interactive and interruptible 3D Touch is.

As I modulate the pressure of the touch on the screen, as you'll see here.

We're using blur and scale effects to let you know that you can continue to interact with this content.

And note how fluid and responsive the entire interaction is.

This is one of the hallmark features that differentiates 3D Touch from traditional touch and gesture based interactions.

So you might be wondering, why should I support 3D Touch in my application?

Well one of the best uses for 3D Touch is that it can quickly accelerate access to existing features that your app already provides.

You saw a great example of this with Home Screen Quick Actions, letting users go straight into those screens within your app just like deep links and let them get straight into the action that they're trying to perform.

But another really great thing about 3D Touch is that it makes possible, brand new immersive interactions that were never before possible.

We're going to show you a great example of this later on, when we go through this new UIPreviewInteraction API.

Finally, we've adopted 3D Touch throughout the system in iOS X.

And so users expect all of your apps to support it as well.

Let's go ahead and talk about how you can start adopting 3D Touch today with Home Screen Quick Actions.

And to do this, I'd like to introduce you to AppChat.

AppChat is a sample app that we've been working on to highlight some of the great features of 3D Touch in our application.

Here you can see that 3D Touch, sorry AppChat is an [inaudible] messaging application.

You can take a photo and send it to your friends as a chat and here you can see I've received a list of different chats from my friends.

I can tap on anyone of them, and view those chats full screen.

Just like that.

AppChat is a very familiar UIKit application.

And we'll show you the different ways that we've added support for 3D Touch to it, starting with Home Screen Quick Actions.

So when you press on the AppChat app icon, we'll reveal some Home Screen Quick Actions for AppChat.

You'll note, I can choose to create a new chat that will open the Camera so I can take a photo and then choose one of my friends to send it to.

But alternatively, I can choose one of these bottom 3 quick actions which are quick actions that will let me send a chat directly to one of my top three friends.

The friends that I communicate with most in AppChat.

So there are actually two different types of Home Screen Quick Actions.

Let's talk about them now.

The first type is Static Quick Cctions, these are specified by your app at build time.

And they're great for fixed actions that are always available in your app.

For example with AppChat, we have the New Chat Quick Action.

I can always create a new chat and then send it to one of my friends.

On the other hand, we have Dynamic Quick Actions.

Dynamic Quick Actions are also great because they allow you to customize and tailor the quick actions that are shown to the user.

But let's take a quick look at Static Quick Actions first.

Static Quick Actions are defined in your apps info.plist file.

As such they're available as soon as your app has been installed on a user's device.

Now let's take a look at an example of how we add a Static Quick Action to AppChat.

Here's our info.plist file, you can see with just a few lines of code, that we are able to create and add this New Chat Quick Action.

I'd like to call out a few things here.

You'll notice we're specifying a type, this is a string that your application defines which you'll use later on when the user selects one of these quick actions to know how to handle it.

We also provide a title, New Chat in this situation.

And, also an icon type which is a constant from a list of system provided icons that you can display.

So in this case that's how we get that nice chat bubble.

One thing to keep in mind is that you'll want to localize any user facing strings in your info plist.strings file as a best practice.

That's Static Quick Actions, let's go take a look at Dynamic Quick Actions which I eluded to earlier.

So these are defined by your app at runtime and provided, from your app to the system at that point.

As a result though, they're only available once the user has launched your app for the very first time.

Now, Dynamic Quick Actions are shown after any Static Quick Actions, space permitting.

You'll only get to display a total of four quick actions on the home screen.

So you'll want to make sure to save room for any Dynamic Quick Actions if you intend to provide them.

You can also include an optional system icon, a custom icon using a template image from your application.

Or you can even create an icon using an address book contact from the user's address book.

This is what AppChat is doing to pull in those nice profile photos, right next to each of those three Dynamic Quick Actions for the top three friends.

Let's walk through some of the code to see how we can implement this.

Well for this example we'll start by looking for a contact that matches our friend Lexi Torres.

The first thing we want to make sure to do is request permission from the user to access their contacts.

Assuming we have that permission, we can go forward, and actually query their contacts for a contact that matches this friend's name that we're interested in.

If we get at least one match, we can then pass that contact to our UIApplicationShortcutIcon initializer.

That will go ahead and create the icon that we'll use with this quick action.

But of course we may not have received access, permission to access the user's contacts, or we may not have found a match for this friend.

So we'll want to have a fall-back, in this case we'll use the system's message style to display as the icon.

Now that we have the icon, we can go and create our actual quick action.

In this case we'll need to specify a type, very similar to how you saw with the static quick action.

As well as a subtitle, we'll use send a chat here, and then we'll pass all of these over to our [inaudible] ApplicationshortcutItem initializer to create our first Dynamic Quick Action.

From here, we can continue to do this as many times as we want, to create any additional quick actions.

And when we're done, we'll package them all up, in a nice array, and hand that over to UIApplicationsShortcutItems property.

This has the effect of registering these Dynamic Quick Actions with the system, so that they can display on the Home screen.

One thing to keep in mind is that this shortcutItems property only displays your apps, only contains your app's dynamic shortcut items or Dynamic Quick Actions.

The Static Quick Actions are only specified in your info.plist and won't be contained in that array.

Let's take a look at how all of this comes together back on the home screen.

Fantastic, with just those few lines of code, we were able to create one Static Quick Action, New Chat, as well as three different Dynamic Quick Actions.

One for each of our top three friends.

There's now one more thing that we need to do though, that's of course to handle these quick actions when the user selects one.

There are two different scenarios to keep in mind here.

The first one is if your app is already running and is being activated, meaning your app is in the inactive or suspended state.

In this case we'll use the callback on UIApplicationDelegate, application performActionForShortcutItem, completionHandler.

In here, we'll use the shortcutItem that's passed in to actually handle the quick action.

Now this could mean many things depending on your app and which quick action you have, but generally you're probably moving the user to a particular screen within your application.

If you handle the quick action, you should make sure to call the completion handler, passing a Bool that indicates whether or not you actually handled it.

Great, that's the first scenario.

The second scenario for us to consider is if the app was actually launched as a result of a quick action.

So in this case our app was not running.

Here we'll use the familiar application didFinishLaunchingWithOptions callback, on UIApplicationDelegate.

Inside, we'll access the shortcutItem key of the launchOptions dictionary that's passed in to see if our app was launched via a quick action.

If it was, we'll go ahead and handle the quick action, just as we would have done before.

And then we'll make sure to return false from this method, that tells the system we did actually handle a quick action here, and it will prevent it from calling the method on the previous slide.

That's pretty much all there is to it in terms of both creating and handling both dynamic and static shortcutItems.

But let's review some of the best practices to keep in mind.

First, every app should provide quick actions, as you see here they're really easy to create and they provide great value, showing up right on the Home screen, letting users go straight to those key activities that your app provides.

As such, you'll want to focus on providing quick access to the highest value tasks that your app offers.

Remember you only have 4 different slots to display quick actions.

So decide wisely which ones you choose.

To help you with that, you might want to use Dynamic Quick Actions, but it's important to keep your quick actions predictable.

Users are going to be quickly opening your Home Screen Quick Actions and choosing one.

And if you're doing things like changing up the order, that'll probably confuse your users and they'll find a frustrating experience.

One interesting thing to remember here is that, you should be prepared to handle Dynamic Quick Actions from a previous version of your application.

The reason is, if you remember, Dynamic Quick Actions don't take effect until your app runs and has the ability to provide them to the system.

So if your app was just recently updated, it will still be showing the Dynamic Quick Actions from the previous version.

As a result, if the semantics of your quick actions have changed, it's a good idea to keep this in mind when handling it and to do this you might want to consider including your app's version number in the info plist, I'm sorry, in the user info dictionary that you can include with the Dynamic Quick Actions.

Finally, try not to add functionality that's only accessible using quick actions.

And remember, not all of our devices support 3D Touch and even on ones that do, users have the option to disable 3D Touch in the accessibility settings of the system.

With that, I'd like to invite Peter on stage to tell you all about Peek and Pop, Peter.

[ Applause ]

Go for it.

Thank you, Tyler.

I'm really excited to talk to you today about Peek and Pop.

As Tyler showed you earlier, Peek and Pop allows people to quickly preview and navigate to content inside your app.

And I can speak from my personal experience, that it changes the way you use the phone.

We're going to talk through Peek and Pop with the sample app that Tyler showed you earlier, AppChat.

As Tyler showed you, in the last section, AppChat is a standard UIKit app.

So tapping on one of the messages, in the table, brings me to the message so I can check it out full screen.

A general rule of thumb for adding Peek and Pop to your app is that if user accessible content can be tapped to navigate to it, it should also support Peek and Pop.

So let's apply that rule to AppChat.

That means that when I apply pressure to one of the cells in this table, we should get a quick Peek.

In our API, we refer to this as preview, because we're getting a preview of the View Controller that we could navigate to.

Now if we continue to apply pressure, that'll Pop full screen, ready for us to interact with.

In our API, we refer to this as commit, because we've committed to navigating to this View Controller.

Cool, so let's go through the components of a Peek and Pop interaction.

First and foremost, we have our registered View Controller, this is the View Controller that contains the user interface elements that we'd like to preview.

In our case, these are our message table cells.

Each registered View Controller has a whole series of sources, these are individual interface elements that each have their own preview View Controller.

In AppChat, this is a natural fit for our chat table cells.

Finally, we have the previewed View Controller, this is the View Controller that will show inside of the preview which represents the element that we tapped in the table.

Usually this is the same element that we'd show after tapping on that content.

So tapping on it, and previewing on it, by applying pressure, shows us the same content.

So with that, I'd like to go step by step with what, how we adding Peek and Pop to AppChat which will closely mirror how you'll add Peek and Pop to your app.

So we're going to start off in our registered View Controller.

Remember this is that table View Controller which contains our presentation's source view.

We're going to conform to the UIViewControllerPreviewing Delegate protocol.

This delegate will be called back at various stages during the Peek and Pop interaction to provide information to the system.

It's really easy to implement.

Next, we'll want to make sure we registerForPreviewing.

A great time to do this is in viewDidLoad.

We're going to registerForPreviewing with ourself as the delegate, passing our tableView as the sourceView.

Because the tableView is the common ancestor for all the interfaced elements that we'd like to preview.

Now let's go ahead and implement the two required delegate methods in the previewing delegate protocol.

The first is for providing a preview ViewController.

This will provide a ViewController for a particular location inside the sourceView.

The method is called previewingContext ViewControllerForLocation, and it's easy to implement.

First, we're going to find the indexPath representing the point that we were passed in by the delegate method.

We can do this by hit testing the tableView.

Next, we're going to create a ViewController with the model object represented by the indexPath, this is logic that you probably already have and did select row at indexPath.

Next, and this is really important, we're going to set the sourceRect of the previewingContext.

Remember, the sourceView is the entire tableView, and we want to make sure to lift off just the cell off the screen, in that beautiful blur and scale effect that Tyler showed you.

By setting the sourceRect the system will appropriately cut out that element and lift it off the screen, it looks really great.

Finally, we'll return the ViewController back to the system.

Now there are two really important parts about this delegate method that I'd like to highlight for you.

The first is that this method will be called every time we think we're going to initiate a preview.

As Tyler said before, we want to keep the interaction fast and fluid, so we're going to call it opportunistically to make sure everything's ready.

As a result, you need to make sure to not take too much time when returning a ViewController from this method.

If there's asynchronous work that you can do in the background, before preparing this ViewController, do that there, and don't block the main queue.

This will keep things feeling fast, fluid, and responsive as we apply pressure to interface elements inside your app.

The second important piece of advice I have for you about this method, is its return type.

UIViewControllerOptional, that means we could return nil.

If you return nil from this method, we won't lift any cells off the screen, we won't do any blurring, we won't play any haptic feedback, we won't do a preview.

But you should make sure to only return nil if there truly is nothing to preview at that location.

You should make sure that similar looking content in your app is previewable in a similar way, this way people won't be squeezing their phones trying to get a preview, because similar looking stuff will be previewable similarly.

Great, so with that, we've implemented the preview part of this interaction.

But we're not done yet.

We need to implement the second required method on this protocol, which is for providing the commit.

This one's even easier to implement, it's called previewingContext commitViewController.

This will be called by the system when it's time to commit, we've crossed that force threshold, we're going to place some haptic feedback, so let's get the ViewController on screen.

Here, we're going to do whatever it is we need to do to get that ViewController on screen.

AppChat is a simple UIKit app that uses navigation controller, so we're just going to call show.

This will have the default effect of pushing the ViewController onto the nav stack.

But there's something really cool about this method.

UIKit has made it so that you can perform any type of ViewController transition in this callback, push something onto the navigation stack, do a custom presentation, even move the ViewController into a custom container.

Do whatever it is you need to do inside your app to get that ViewController on screen, and UIKit will handle the animation on your behalf.

So great, we've implemented commit ViewController, and now we've got that awesome commit, which will allow us to navigate full screen and begin interacting with that content.

Now we've added Peek and Pop to our app, but we can do a little bit extra work to take the experience, to the next level.

Another aspect of Peek and Pop that's really cool is preview quick actions.

And by adding these to your app, you can allow users to access the most common actions with the content shown inside the preview.

This is really powerful and allows people to use your apps even faster than before.

If we go back to our familiar architecture diagram, we'll see that these preview quick actions are owned by the previewed ViewController.

This is the ViewController that's capable of responding to these actions because these take action on the content shown in that ViewController.

These are easy to add.

All we have to do is override the preview action items function, in UIViewController.

This returns an array of UIPreviewActionItems.

We can create UIPreviewActions using API you're already probably familiar with.

It resembles UIAlertAction and UITableViewRowAction.

You just pass a title, a style and a closure, and we'll call that closure when the action is selected by the user.

Next, we'll just return that array of actions back to the system.

Now we've got one action here for applying with a heart, but AppChat lets you reply with a whole bunch of emoji.

So what we really want to do is group those emoji into one group action.

We've got API for that too, through UIPreviewActionGroup.

All you have to do is create an array of preview actions; here we have them for all of the emoji you can respond with.

And then create an action group with a title, style, and the array of those actions.

This is a great way for you to group related actions inside your preview quick actions.

There are two other important parts for the preview action API that are also handy.

And this is through the style enumeration on preview action item.

The first is the selected style which will show a little check mark next to that item, to let the user know that they may have already selected that option previously.

Now before we came out with beta 1 that check mark was feeling a little shy, so you'll be noticing that it's missing from the first seed but rest assured that that bug will be fixed.

The other style that's really handy to use is the destructive style, which we can use to indicate that an action may perform a destructive act.

You'll see that here behind me with the block action.

So by adding Peek and Pop, and preview quick actions, we've taken the experience inside your app to the next level and sped up the interaction that your users can have.

Let's review some best practices for adding Peek and Pop to your app.

First and foremost, remember the rule of thumb, content that can be tapped should also support Peek and Pop.

You can look to Apple's system apps for a great hint at how to do this.

Next, make sure to return a preview view controller consistently from the delegate call back.

Similar looking content in your app should be previewable in a similar way.

In that same previewing context callback make sure not to take too long.

Remember, we don't want to block the main queue because we want this interaction to remain fluid and responsive.

Make sure to set the sourceRect of the previewing context, to lift the appropriate user interface element on screen.

So, that's it for Peek and Pop.

Now I'd like to turn our attention to some super cool new API in iOS X.

UIPreviewInteraction.

UIPreviewInteraction lets you take the Peek and Pop feel, which is force processing and haptic feedback, but bring your own user interface.

And as we'll see in a moment, this a really powerful concept.

Let's look at an example.

In AppChat, in our chat detail view, we've got this handy Reply button at the bottom.

On a non-3D Touch enabled device, we can tap on that Reply button to get a whole list of the emoji we can reply with.

And then we can tap on one of these emoji, to send it back to our friend.

But on a 3D Touch capable device, we can make this interaction a lot more fluid and a lot more fun.

With just a few lines of code using UIPreviewInteraction, we added something really neat.

We can apply pressure to the Reply button and interactively present that sheet, notice how the blur was fading in and out.

Then while keeping my finger down on the screen, I can slide around to pick an emoji to reply with.

Hopefully we don't pick the devil.

And then we'll send it back to our friend.

This type of one touch interaction is something that 3D Touch is perfect for.

Another thing that 3D Touch can help us with is accelerating actions that can live closer to the user's finger.

In iOS 7, we added the slide back gesture as an alternative for the Back button.

And this is more convenient because it's right next to where your finger is, you can just move it over to the edge of the screen and slide back.

And by using 3D Touch, we can add that convenience to your app's interactions.

So we can actually 3D Touch anywhere in this photo to interactively bring up that reply sheet and then we can actually commit that reply sheet open, by applying some more force, and then pick an emoji to reply with.

This type of rich interaction that can make things feel a lot more fluid, and a lot more fast, is exactly the type of thing that we hope you'll build with UIPreviewInteraction.

UIPreviewInteraction takes the same Peek and Pop force processing that we've honed for the system provided Peek and Pop UI, and the automatic haptic feedback as we move through the stages of the interaction, but allows your app to bring the user interface.

And what's really exciting about this is that people can now build muscle memory for Peek and Pop, and apply that same muscle memory in the system standard previewing appearance, but also in the custom interactions that you'll build with UIPreviewInteraction.

And that muscle memory will be universally applicable, this is really cool.

So let's walk through step by step how you might add preview interaction to your app.

We're going to start off by conforming to the UIPreviewInteractionDelegate protocol.

This delegate will be informed at various stages during the preview interaction to let you know what's going on.

Next, we'll want to make sure to create our preview interaction.

Again, viewDidLoad is a great time to do this.

Here, we'll create our preview interaction with a sourceView and set ourselves as the delegate.

Now, preview interaction is all about state transitions, so let's talk through how these state transitions work.

When the interaction starts, if your delegate implements the optional preview interaction should begin callback, we'll call that, or if you don't, we'll begin the interaction.

Now, as the force moves from the beginning of the interaction towards the preview state, we'll message back your delegate with previewInteraction, didUpdatePreviewTransition :ended.

Check it out.

These force progress updates are given as a normalized value from zero to one.

And what's really important to highlight is this is not just a direct translation of force.

Because it's using the algorithms that we developed, for Peek and Pop, we're actually detecting the user's intent.

So this is not just some simple force translation.

As we move between these two force states, we'll be notified at every step along the way with our current progress through the interaction.

And once we reach the target state of preview, we'll hear about it because the progress will move to one, and will passed true for the ended parameter.

And, the device will automatically play haptic feedback.

Let's go through a sample implementation of this method.

Again the method is previewInteraction, didUpdatePreviewTransition :ended.

And here we're just going to update ourselves for the current progress through the transition.

Now notice, this is actually plug and play with a number of UIKit technologies.

We can use it to drive a UIViewPropertyAnimator, a percent driven interactive transition, or even a UIKit dynamic system.

These instantaneous updates for the state transitions work with all these technologies.

And if the interaction ends, we'll want to show our completed preview appearance.

This is the first required delegate method on UIPreviewInteraction.

The second is for responding to cancellation, it's called previewInteractionDidCancel.

This will be called whenever the user lifts their finger or if the interaction should cancel for any other reason.

For example we got a phone call, here we'll set our progress back to zero, and reset ourselves to our initial appearance.

But notice that we're doing this inside an animation closure, we'll want to animate ourselves back to rest to avoid things flashing around when we lift our finger.

So that's how we can respond to the preview state transition.

But remember in AppChat, we also want to respond to the commit state transition for sticking our content on screen.

We can implement the optional delegate method, previewInteraction didUpdateCommitTransition :ended, to respond to our progress through commit.

So, when we're between the beginning of the interaction and the preview state, we'll call back didUpdatePreviewTransition.

And once we reach the target state of preview, we'll pass a progress value of one and an ended value of true.

And we'll also play some haptic feedback, then we'll begin sending messages to didUpdateCommitTransition :ended, with the progress from preview to commit.

This is the same sort of progress update normalized values from zero to one.

And, once we reach the target state of commit, we'll pass a progress value of one ended as true, and we'll play another different piece of haptic feedback.

Let's go through a way in which you might implement this method.

So in our implementation of previewInteraction didUpdateCommitTransition :ended, here we're going to update our progress for commit.

Again, this works with the whole suite of UIKit animation and dynamics API.

And once the interaction ends, we'll just show our completed commit appearance.

So that's it for UIPreviewInteraction.

Now if you're building a game or a drawing app, there's some low level force API that you can use.

This API provides normalized access to the force values coming in with each UITouchObject, through two properties on UITouch, force and maximumPossibleForce.

These values will be populated on all devices that support 3D Touch, along with touches coming from an Apple Pencil that support Apple Pencil.

And we won't get into them in more detail here, instead, I encourage you to check out the video for the leveraging touch input on iOS Talk.

It's a really great talk and goes into these APIs in depth.

So let's review.

As Tyler showed you earlier, Home Screen Quick Actions allow your users to jump straight into action with many of the common activities inside your app.

And most applications will really benefit from adding Home Screen Quick Actions.

Next, I showed you how Peek and Pop allow seamless fluid and quick interaction like never before and lets people interact with your app in a whole new way.

And we think that many apps will really benefit from adding Peek and Pop.

Finally, we looked at UIPreviewInteraction, which allows you to explore new depths in the ocean that is 3D Touch, and really take a great look into the next dimension of multi-touch.

Finally, it's important to note that users will expect all apps on their device to support 3D touch so we would highly encourage you to adopt these features.

For more information and to download the AppChat sample app that we showed you throughout this talk, which uses all these technologies, check out the address behind me.

There are a few related sessions, one on the new UIKitPropertyAnimator API, which works great with UIPreviewInteraction.

And another for leveraging the lower level touch input on iOS.

And that's it, thank you so much, and have a great rest of your conference, thank you.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US