Developing tvOS Apps Using TVMLKit: Part 1

Session 212 WWDC 2016

Join us for an introduction to TVMLKit, a template-based UI layout technology that is part of tvOS. Learn about new features coming in tvOS 10, as well as best practices in architecting your application, styling and customization of templates, and media playback.

[ Music ]

[ Applause ]

Good afternoon and welcome.

My name is Nurinder Manj and I'm the Engineering Manager of TVMLKit team.

I'm here with Parry Panesar to talk about how to build great apps using TVMLKit.

Now most of you here have built one or more native apps and gone through the hard work of performance tuning and optimizing user interface.

Often that means sacrificing features or delayed shipping.

Some of you have built web apps which offer flexibility, dynamic updates, but the user experience is not so great.

TVMLKit, which is a brand new framework introduced for tvOS, bridges the gap between these two development choices.

When you build your apps using TVMLKit, they are going to look, feel, and perform like Apple apps.

Let's get started.

Today, first I'm going to talk about what the framework offers you.

Next, we have sample app built for you that is going to demonstrate a fully-featured TVMLKit app.

But before that, I will cover the overview of the framework.

The latter half of the talk is dedicated to talking about some of the exciting new features we are going to introduce for tvOS 10.

Let me walk you through some of the user interface examples.

This is an app store app built into Apple TV, a gorgeous iTunes movies app, an example from Search app.

Another example is Apple Music.

In fact, all of these apps that ship with Apple TV are built using TVMLKit.

And if you take a step back to consider what are all these apps about, they have a common goal.

They let people browse, interact with, and consume catalogues of content in a simple, consistent but unique way.

So what does the framework offer?

The framework offers template-based native user interface, which is performance tuned and optimized, saving you both development cost and time.

That rich and complex user interface that you just saw is defined using a simple markup API and JavaScript drives the application.

Templates are very flexible.

You can change their appearance and behavior using styles.

Now the framework is not constrained to the templates that we provide.

You can enhance existing Apple templates by adding your own views and view controllers to them or even create brand new templates.

You can also extend JavaScript functionality that is specific to your app.

And let me assure you this is not a web browser.

It an optimized native user experience built for tvOS.

Let's build an app.

There are three main components that I'm going to talk about that we are going to need built our sample app.

First is Xcode to create and configure the client project.

Next, I will talk about the Markup and the styles to configure the templates.

And last, I will talk about the JavaScript API, how it will drive the application.

Now before we dig into details, let me show you what this looks like from an architectural perspective.

A typical TVMLKit app is based on a client server architecture model, where your client app is a UIKit-based app running on a tvOS device and your server hosts the media content and JavaScript files.

The framework offers an application controller API that will download the main JavaScript file and set up a JavaScript environment and hand over the control to JavaScript.

Once the control is handed over, JavaScript is responsible for downloading any additional content that it needs to present the templates to the user.

And JavaScript is also responsible for handling any user events and reacting to them appropriately.

Now the first step to build an app is Xcode.

Xcode provides TVML application template which will act as a starting point for your app.

This is the partial code that gets generated by the template.

The first step here is to modify the JavaScript URL to point to your main JavaScript file.

Next step is to create an application controller context, associate the javaScriptURL, and you can optionally also specify the launch options that will be passed on to the JavaScript when it is launched.

And the last step is to create an application controller instance with the context and window, and that will download your main JavaScript file, evaluate it, and hand over the control to JavaScript.

You can also optionally specify the delegate to monitor the state of your controller, for instance if it launched successfully or failed.

Next let's talk about templates.

Now TVML is a Markup specification to define Apple-TV-specific templates.

The Markup when presented on the navigation stack from JavaScript translates into user interface.

Now once it is presented, you can keep modifying your XML, and those changes will be propagated to user interface in a performance way.

Templates out of the box just look right.

You don't have to do any explicit styling.

Just fill in your content and you are good to go.

However, you can change the appearance and behavior using styles to suit your [inaudible].

In terms of Markup, document is the entity that wraps the templates.

This is the XML structure representing a document.

There are two child elements.

The first is head, which has a style, which encapsulates all the custom styles defined by you.

The second element is the templateName and its content.

Each template has a unique name and functionality.

Let's take a look at one of these templates.

This is the Top Movies page from iTunes Movies app.

This template allows you to scroll through stacks of content.

In Markup, it is called as a stackTemplate.

In this particular example, this template is represented using this XML.

At the root of the template is a collectionList element which encapsulates various collection types, like carousel, shelves, grids, and other supported types.

Now the top row here is a carousel control which allows horizontal navigation of content with the focused one in the middle.

This control when in idle state will auto scroll the content, and the Markup to create this control is simply this, where you have a section defined and list of lockups with imagines.

There is no any additional configuration required.

The row next to the carousel is a shelf which also allows horizontal scrolling of content.

And the Markup to represent that is very similar to carousel, but shelf allows you to specify a header and can have one or more sections.

Each section further can also have a header and must have one or more lockups.

So what is a lockup?

A lockup is a UI control that encapsulates image and text elements.

This is a [inaudible], by the way.

And going back to our example, if you notice, only the lockup that is focused has its text element visible.

This is not the default framework behavior.

This is an example of how you can customize using styles.

Let me show you how to do this.

Here we are defining a new class which has a highlight style property set to the value to marquee and show when highlighted and then you set this class not the appropriate text elements of the lockup and that gives you this behavior.

However, this is a fairly simple example of customization.

Let me show you a couple of more examples.

This is a stackTemplate again.

Here we have a banner which has a background image specified and banner is a sibling to collectionList.

There's a background color specified on the template, and the template seamlessly blends the background color into the banner image giving this nice effect.

The layout for the banner title and the button is custom-configured using styles.

Another familiar example is a movie bundles page.

This is also a stackTemplate.

The background image on this template is created by the framework by just specifying an image as a hero image element.

And again, the layout for the title, the shelf, and the buttons is custom configured.

Now there are numerous templates available for you to pick from.

And using styles, you can create unique experiences that suit your brand.

TVML Catalogue is a great resource to browse and interact with templates.

Please check it out.

Now as I mentioned before, TVMLKit bridges the gap between native and web development.

You just saw how easy it is to create rich and complex user interface by simply using Markup.

Let me show you how the JavaScript drives the application.

The JavaScript engine that is provided is powered by JavaScriptCore framework and the built-ins in Web Inspector to debug and performance tune are provided.

And starting tvOS 10, JavaScriptCore is ES6 compliant as well.

[ Applause ]

TVMLKit adds platform-specific APIs to help drive the application.

Now the standard web APIs like XMLHttpRequest to transfer data between client and server; DOM, to parse and [inaudible], listen for user events, local storage, session storage are all provided by the framework.

In addition to that, TVMLKit also provides platform-specific APIs, for instance to manage the navigation stack, access system info, play [inaudible] slideshow photos, and many more.

In addition to that, the framework also provides very simple API to handle media playback.

Now one important thing I would want to point out is that there is a single JavaScript context per application controller, and you manage all of your documents within that single context.

Let me go into details for some of the key classes.

The first is app object.

App object provides callbacks that you must register and the first and foremost is onLaunch.

This is the starting point for your JavaScript application.

Another API I would want to point out is to handle errors, which is onError.

You must provide this callback as well, to handle those unexpected failure cases.

Now once the control is handed over to JavaScript, the first thing you will want to do is probably present a document.

To do so, NavigationDocument is the class you would want to work with here and it's equivalent to a UINavigationController in TVMLKit.

There is a global instance already provided and you can now create new instances.

Now you're only responsible to push the documents, [inaudible] them as a result of Menu gesture is handled by the framework.

There are three main APIs that you will be using the most.

The first two here which is pushDocument and replaceDocument go hand in hand.

Let me show you a recommended way on how to use these.

Here is a code snippet to create a loading template.

Typically when you are handling a user event or launching the app, you would want to present a Spinner so that the users know something is happening.

And once you have created this loading document, you would use pushDocument to push it on the stack.

Now while you are waiting for your remote data to be fetched and prepared, later on you would want to replace your loading document with your intended document.

And to do that, you would use replaceDocument.

This is a very common pattern, and we encourage you to show the Spinner right away while you are fetching the data from the network.

And the last API is presentModal to present alerts or errors or actually any other templates modally.

Now the key to TV watching experience is video playback and we have made it really simple.

Let me show you how to do that.

With less than 10 lines of code, you can configure a fully-featured video playback experience.

Let me walk you through this code.

First, create a MediaItem with the type of video and the URL to the video stream.

You should also specify title, artwork, URL description so that it can be made available in the info panel of the video playback experience.

Next, create a playlist and add one or more media items to it.

And the last step is to create a new player instance, associate the playlist, and present the player.

And that gives you a fully-featured video playback experience.

In addition to that, all the features like to handle interstitials, protected content, chapter groups, time [inaudible] and a bunch of other APIs are also provided from within JavaScript environment.

Now similar to video playback, we have also extended the audio playback capabilities and doing so is very similar.

The only thing changed here is tied to audio and the URL to an audio stream.

And that gives you this beautiful and functional Audio Now Playing experience, which is the same as Apple Music.

Now if you're building a music app, there is one more step that you should do which is to configure your app so that it can continue playing back audio while it is in the suspended state and doing so is very similar to how you have done it on iOS, which is to set up a playback category on the AV session and add the background modes to the info.plist and the rest is handled by the framework.

And with that, I would like to invite Jeff on stage to demo Building a TVMLKit application.

Thank you.

[ Applause ]

Thank you, Nurinder.

Hello, everyone.

My name is Jeff.

I'm here to show you a TVML application that highlights some of the things that we've talked about so far.

You'll also get a sense of how easy it is to build a native application for the Apple TV using TVMLKit.

So for this demo, we'll be showing you a simple version of the WWDC app.

In terms of the user interface, we'll have multiple shelves of content where each shelf will contain session videos for a single track in the conference.

So just imagine having a shelf for developer tools, app frameworks, design, and so forth.

So let's get started.

So this is the Xcode project that we had prepared earlier.

Let's start with the AppDelegate first, which is our app's entry point.

In the application didFinishLaunchingWithOptions, as you can see in just a few lines of code, we have bootstrapped this application as a TVML application.

We also specified the JavaScriptApplicationURL here, which happens to point to a server that is running on this Mac.

So if you look at this directory over here, this is where I've configured the server to serve files from.

And as you can see, there is a file here called application.js which is the JavaScript file that drives the TVML application.

There is also a JSON file here which will contain all the data that we need for this demo.

So let's work through the application.js file.

So at the top of this file here, we have declared some variables to hold our model data.

Next in the App.onLaunch function is the function that gets called when TVML application starts up.

So the main thing that we want to do on here is to fetch our JSON file and then create a user interface using the data.

Now before we go ahead and do that, we should really be showing a loading indicator on the screen.

This is always recommended for better user experience while something is fetched over the network.

So let me add a few lines of code here.

So the way to do this in TVMLKit is to create a loading template document.

And this is done in this function called createLoadingDocument.

Let's jump into this function and you will see that we are basically creating the Markup for a loading template and then creating a DOM document object using the DOMParser.

Now returning to the launch function, after creating the loading document, we will now use the navigationDocument object to push the document onscreen.

So this is like using UINavigationController in UIKit.

So now that we have a loading Spinner visible on the screen, we will now call a function that I created called request JSON, which will use the built-in XMLHttpRequest object to fetch our JSON file and then returning a response in a callback.

Next, we will convert the JSON text that we get back into our JavaScript data structures.

Now as our UI calls for a stack of shelves, we will be creating a stackTemplate document.

So let me paste some coding here and jump into our createStackDocument function.

Now as you can see, similar to the way we created the Markup for the loading template earlier, we are creating the Markup for a stackTemplate here.

So within the stackTemplate Markup, we've added a top banner image here.

And within the body of collectionList, we are actually looking through each track in the conference and creating a shelf element for each of them.

And this is done using the createShelfElement function that I have here.

So let's jump into this function.

And again, we are creating the Markup for a single shelf here.

And within its body, we are creating a lockup element for each session video.

So let's now jump into the createLockupElement function and you will see that we are creating the Markup for a single lockup that describes the session.

And in this case, we are capturing the sessionId as a custom attribute.

We are also adding an image with the width and the height and a title.

So just a recap, we have now created a stackTemplate document that contains multiple shelf elements, and within each shelf element, we have multiple lockup elements.

So now let's return to the launch function.

So after creating the stackDocument, we will now use the navigationDocument object to replace the loading document that we have currently visible with our stackDocument.

And with that, we are now ready to run this.

Let's switch to Apple TV.

So there you go.

We now have a beautiful UI of a stack of shelves, [ Applause ]

where each shelf contains image lockups or session videos.

So as you can see, with just a small amount of code, we were able to create an application that looks great and feels right at home on the Apple TV.

So the next thing that we want to do is to be able to play a video when we click on a lockup here.

At the moment it just does nothing.

So let's head back into Xcode and add this functionality.

So back into our launch function, I will now add these two lines of code.

So this will register for the select and play events.

The select event is when you press on the trackpad of the Siri Remote and the play event is triggered when you press on the Play button.

And in both cases, my playSelectedLockup function will be called.

So let's jump into this function and implement this.

So the first step is to retrieve the lockup element from the event.

From there, we can retrieve the session using the sessionId custom attribute.

Next, we will create a new mediaItem object, whoops, of the type video and initializing it with a URL of the video, as shown here.

We will also populate other information about the session such as the title, description, track, and artwork.

Next, we will create a playlist object and push our mediaItem object into it.

And finally, we will create a new player object, set the playlist, and then simply call play to play the video in full-screen mode.

So just a recap, we have now created a player object with a playlist that contains a single video item.

So with that, let's see this in action.

So back on a UI, we will now use the Siri Remote and click on the lockup and the video should now start playing in full-screen mode.

[ Music and Applause ]

Thank you.

And you can press the Menu button to exit the video as well.

So that was video playback for TVMLKit.

Thank you for your time.

Back to you, Nurinder.

[ Applause ]

Thank you, Jeff, for the amazing demo.

But just little amount of code, you could build a fully-featured app which is performance tuned and polished.

The spacing between the lockups, rows, shadow treatments, text treatments all just looks right.

Before we continue, let me recap what you just saw.

Using the TVML application template in Xcode, create and configure your client project.

You learned how to create a simple loading document and a complex stack document from JSON.

And you learned how to use NavigationDocument to push or replace documents on the stack.

JavaScript is responsible for handling user events.

And in this case, we handle select and play events and configured video to playback corresponding to that lockup.

Now when we introduced TVMLKit, you could already create great apps and a lot of you already did.

So thank you.

You have provided some great feedback and we wanted to make it even better.

We have been hard at work and are adding some exciting new features that are easy to adopt and would help take your app to the next level.

To talk all about them, I would like to invite Parry on stage now.

Thank you.

[ Applause ]

Good afternoon.

My name is Parry and I'm going to walk you through some of the new features we've introduced in TVMLKit in tvOS 10.

Now TVMLKit provides you with highly desirable app-level features through a simple yet flexible interface.

And the new features we've added in tvOS 10 are no different.

For example, simply by adding one attribute in the search results in a search document, you can make the results animate in and out as the user is typing.

This provides an engaged experience to the user as they are trying to narrow down the results.

Or if you have a music app, simply by adding one menu item to the top-level Menu bar, you can enable the presentation of Now Playing audio experience there, all managed by TVMLKit.

So it comes in and goes out as the playback starts and stops.

This gives your users a convenient way to return back to the playlist and continue listening to music.

Now all of these features are really powerful, but they're also very easy to implement.

And to show you that, I'm going to take three of these, talk about them, and at the end, implement them in the demo you just saw.

So let's start with the first feature I want to talk about, light and dark appearance.

In tvOS 10, there's a new dark appearance that's implemented systemwide and all apps should adopt it.

Your TVMLKit app is going to opt into this by configuring the applications info.plist.

It's the same thing you would do is you were writing an application using UIKit.

So you'll specify the appearance you want for your app.

You can say either light, dark, or automatic, in which case it picks the system preference.

But no matter which option you pick, your standard TVMLKit app is going to work right out of the box, without any further modifications.

All of our templates are configured with styles for both appearances, light and dark.

And TVMLKit switches between them as and when required, so you get it for free.

However, if you have explicit styles in your document, then you will have to specify those styles for both appearances as well.

Let's take an example.

So here's a template that has a title and its color is set to black using an explicit style class foo.

This may not work in dark appearance.

And for it to work, you would have to define the class foo specific for an appearance like this.

So in order to do that in tvOS 10, we've added a new media feature called tv-template that has a feature called tv-theme using which you can create a media query that is specific to an appearance.

So simply combine all your styles up for both the appearances, group them in their respective media queries, put them in the document, and TVMLKit will pick the right styles for you.

So with little configuration in the style and zero lines of code, your apps are ready for dark appearance.

Now let's move on to something else that also enhances your user experience but in a different way.

Let's talk about embedded videos.

It's fair to say that videos are a major part of TV watching experience.

And if you have a catalogue of rich video content, then you want your users to experience that as soon as they launch your app.

And embedding videos in your app creates a big impact.

Now just to show you what a difference it can make to your app, we created a small sneak peek of the demo you just saw but with embedded videos in it.

Have a look.

[ Music ]

It's seamless.

Not only it's not obtrusive to the browsing experience but it compliments it.

It makes it more immersive.

And as expected, TVMLKit does most of the heavy lifting for you and gives you this nice high-level interface to work with it.

So let's have a look at that.

So in a nutshell with TVMLKit, you get to embed a player and a playback area inside your documents.

Most likely it's going to be inside a lockup.

TVMLKit handles the playback for you and all you have to do is specify when do you want it to begin.

So you can say that it should begin either when the containing lockup gets focused or as soon as it appears on the screen.

You have full control over transitioning the embedded video to full screen.

And if you have advanced use cases, encryption using FairPlay for instance, then you have access to the embedded player in JavaScript with all of its APIs so you can implement that.

Now you can embed videos using TVMLKit in three easy steps.

First, you configure the template.

Second, you configure the player with the media item you want to embed.

And third, you want to handle triggering the transitioning to full screen.

Let's have a look at these in a little bit more details.

So we've made configuring the template for you very intuitive.

We've added a new element in TVML called mediaContent that you can use the wrap the image of the lockup in which you want to embed the video.

This gives the lockup the same behavior and look before the playback begins.

And the image inside the lockup provides the bounds in which the video will play.

You can specify the playback mode as an attribute on the mediaContent as well, so it will be as soon as the lockup gets focused or it appears on the screen.

Here's an example.

So here's a lockup that has a mediaContent which wraps the image of the lockup.

And the width and height on the image provides the bounds in which the video will play.

Second step, configure the player.

Now each one of these mediaContent elements comes with its own player, and you can access that player in JavaScript.

Simply query for the player feature from the mediaContent's DOMElement and configure the playlist with the media item you want to embed.

Here's a small code for that.

Now if you notice carefully, the configuration of the player is not that different from what you saw in the demo.

You create a playlist, you add the media item, and you set it on the player.

But there are two differences.

One, you don't have to create a new player.

You just use the embedded player.

And two, instead of doing it when the user selects the lockup, you do it well in advance.

In fact, you do it even before you push the document, which brings me to an important point.

For better user experience, you must configure the player before you push the document.

Once the document is loaded, you can change the playlist at any time you want.

And finally the last step, transitioning to full screen.

Now it's worth noting the TVMLKit will not trigger the transition for you.

It's one of those flexibilities that we want you to have.

So you'll add an event listener for select and play for the lockup and use the embedded player to trigger the transitioning to full screen.

Here's a small example.

So here, I have added an event listener on select event on the document.

I'm grabbing the mediaContentElement from the subtree of event target.

Now this would be the lockup because all events get dispatched on the lockup.

And finally calling present on the embedded player to trigger the transitioning.

Now you have full control over when you want to do this, but transitioning back to embedded mode happens automatically on a Menu gesture.

So that's it.

With these three easy steps you can make your user interface inside your apps so much more immersive.

But what happens next?

What happens when the users are already in full screen?

They're watching your content.

How can you make that more engaging?

Well that brings me to the third feature I want to talk about today that lets you do exactly that.

Interactive video overlays.

Now in a sense, it's a counterpart to embedded videos in that it lets you enhance your content by putting a user interface on top and you can do so much with it.

For example, you can suggest more options to the users so that they can pick from it and continue watching.

You can let them skip past teasers and credits and implement a perfect binge-watching experience for them.

Or you could implement in-app purchase right on top of your content while the users are immersed in that experience.

Even if you do it with something as simple as interactive metadata, like Cast, you make your content so much more engaging and interactive.

And as you've seen with other features, implementation is really simple.

You basically create a document, use any template for it, and set it on the player, and TVMLKit will present that document when the video goes full screen.

Here's the code for that.

We've added a new property on the player in tvOS 10 called modalOverlayDocument.

So when you set the document on this property, we will present it for you on top of full-screen video.

If the player is already playing in full screen, your documents will get presented immediately, but if the player is not playing or if it's playing in the embedded mode, they'll get presented when the video goes full screen.

So it's really convenient for you to just set it at any time you want.

And with that, let's have a demo.

Let's implement these features in the demo you saw earlier in the presentation, and to do that, let's welcome back Jeff on stage.

Jeff.

[ Applause ]

Thank you, Parry.

Hello again.

Let me show you the new stuff.

So back on the Apple TV.

So this was the demo that we showed you earlier.

Now let's go Home and hit into the Settings app.

Let's navigate down to the new Appearance setting here.

We will now toggle the appearance to dark.

So let's go Home again.

Now because we created the demo app using Xcode 8, we will have the entry in info.plist to support automatic user interface dock.

So what this means is our demo app is going to support dock appearance automatically.

So let's see how this looks.

Boom. There you go [applause].

Look how beautiful our UI is in dock appearance as well.

And again, zero code to support this.

Now let's talk about embedded videos.

For our demo, we will like the video to start playing automatically inside the lockup right there.

As Parry mentioned earlier, there are three main steps to achieve this.

Step one is to use the mediaContentElement tag.

Step two is to configure the embedded player.

And step three is to handle full-screen playback.

Let's get back into Xcode.

So let's get into the createLockup function.

Now instead of creating a standard image lockup right here, I will now add the new mediaContent element tag and have it wrap our image.

That is it.

So that is step one.

Pretty easy.

Now onto step two.

Now as Parry also mentioned earlier, each mediaContent element is going to come built in with an embedded player.

So what we will need to do is to configure these embedded players with a corresponding playlist before we present the document.

So let's head back into the launch function.

Now before we present the document, I will now call a function that I created called configureMediaLockupElements.

And inside this function, we are first retrieving all mediaContent elements from this document.

And for each mediaContentElement, similar to the previous demo, we will be creating a new mediaItem object, a new playlist object that holds our mediaItem object, but instead of creating a new player right here, we will use the getFeature function to retrieve the embedded player and then we just set the playlist on it.

So that was step two.

The final step is to handle full-screen playback.

So let's head into the playSelectedLockup function, which as you recall is triggered from the select and play events.

We won't be needing all of this code here, so let's replace this code with this.

First we are retrieving the lockup element from the event, then retrieving the mediaContent element from the lockup element, and again using the getFeature function to get access to the embedded player, and then we simply call present on it to take it full screen.

So let's see how this looks.

So as you can see, [Background Music] the video will now start playing automatically inside the lockup right there.

And when I clicked on the lockup, you will now transition to the video into full-screen mode.

Welcome to 2015 Apple Design Awards and welcome your host for this evening Pretty cool stuff.

You can also press the Menu to exit the full-screen mode.

So that was embedded videos.

The next new feature we wanted to show you is interactive video overlays.

So this new feature is going to allow you to place a UI over the video while it is playing.

So for our demo, let's say we want to show a single shelf of related videos on top of the video.

So let's head back into Xcode.

Now in my playSelectedLockup function, I will now call a function that I created that creates my overlay called createInteractiveVideoOverlay.

And inside this function, as you can see, we are basically creating another stackTemplate with a single shelf, and within the shelf, we have multiple lockup elements.

Also, we are changing the style of this stackTemplate.

We are adding a blurred background and we are also adding some padding at the top so that our shelf stays positioned at the bottom of the player.

So let's head back into playSelectedLockup function.

Now after we create our overlay document, we will now set it to the new modal overlay document property of player and the player object will just show it.

In fact, the player will show any TVMLKit document using this property.

Now we can also show this overlay at any time while the video is in full-screen mode.

For example, we might want to show the overlay 30 seconds before the end of this video, but for the purpose of this demo, we will just show the overlay as soon as the video goes full screen, which is why we called it here.

So let's run this.

Again, the video is going to start playing automatically inside the lockup [Background Music].

And when I now take the video full screen, you will now see the overlay on screen.

[ Applause ]

And the overlay is also interactive so you can pan around within the lockup in the shelf here.

So that was interactive video overlays.

We hope the demo has been useful to everyone.

Thank you for your time.

Back to you, Parry.

Thank you, Jeff.

That was an amazing demo.

Let's quickly recap what we saw in that demo.

You saw how light and dark appearance just works right out of the box for TVMLKit apps.

You saw how easy it is to embed videos in three simple steps.

And finally, you saw an example of a binge-watching experience implemented through interactive video overlays.

Now with that, I would like to summarize what we've learned today.

So there's one thing I want you to take away from this presentation and it's this.

TVMLKit is an easy way to make apps on TV that gives you native experience which looks and performs like Apple apps.

It's based off of web technologies like Markup and JavaScript that lets you develop your apps rapidly and reduce the time to market.

And with the new features and how you saw how TVMLKit takes the onus off of you for writing the user interface, you can spend your time, money, and intellectual energy on the features and content that make your app unique and great.

I highly encourage you to go to the developer website and check out documentation and download sample code.

There's a wealth of information there for you all.

I'd also like to suggest some related sessions including What's New in tvOS, and one in particular that going to happen tomorrow, which is TVMLKit, Part 2, and we talk about how can you mix your own views, view controllers, and even JavaScript APIs in TVMLKit.

And finally, I would like to thank you all for being here and I wish you have a very nice rest of your WWDC16 experience.

Thank you.

[ Applause ]

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