Application Development for tvOS

  • Tutorial
The other day, Russian users who ordered the Apple TV set-top box on the official website finally began to receive the long-awaited devices.

As you know, in the new generation of consoles, the App Store will debut with third-party applications. The first Russian reviews sadly noted that there are not very many applications in the Russian store (and this needs to be fixed somehow!) . However, among them there is a Rutube application. In this article we will share a little experience that igor-petrov and Juraldinio managed to acquire during its development.

About tvOS platform


The new Apple TV (tvOS) platform is an iOS derivative and has some unique features. In particular, for tvOS, it is possible to port an existing iOS application written in Objective-C / Swift (according to recommendations for implementing the interface ), and to create an application on a new technology stack - TVML and TVJS, connected using the TVMLKit framework. This new stack is positioned as a good choice for simple and quick development of client-server applications (ideal for video!) , Which is confirmed in practice. It is about TVML and TVJS that will be discussed below.

TVMLKit


TVMLKit is a framework for Objective-C / Swift. With its help, application control is transferred to the JavaScript file and TVML + TVJS environment. Details are available on the TVMLKit documentation page .

Tvml


TVML (Television Markup Language) is a subset of XML that implements markup and standard tvOS interface elements. This interface is used not only for applications - it implements all tvOS pages: the home screen, settings screen, and the App Store itself.

For example, the button in TVML looks like this:


And so the button looks on the screen:



And so the device’s home screen looks, consisting of TVML elements:


tvOS provides a set of ready-made elements, a complete list of which is available in the TVML documentation .

These elements are divided into:

  • Simple elements - simple elements consisting of one tag, for example, text or title;
  • Compound elements are composite elements that can include simple elements. For example, a composite banner element may contain a simple title element;
  • Templates - page display templates. Define a general structure for displaying elements and may include composite elements.

In addition, attributes and styles can be specified for elements, such as width and src .

There are limitations - certain types of composite elements can contain only certain types of simple elements, the same applies to templates. In general, the structure of the elements seemed rather confusing - each time I had to look into the directory to understand where which element could be used.

From the general list of templates, it is worth highlighting divTemplate , which allows you to design your own layout, if you are not satisfied with any of the other proposed ones.

There is no opportunity to create your own elements, you can only combine existing ones.

Moreover, you need to make sure that when parsing strings in XML, invalid TVML does not come across. For example, our API response might contain HTML tags, which caused an error.

TVML markup can be dynamically added to a page using JavaScript and TVJS.

Tvjs


TVJS is a JavaScript framework that provides an API for working with TVML. Conditionally, it can be divided into two parts:

  • Standard DOM Level 3 API for working with DOM elements, for example, the DOMParser constructor for parsing a string into an XML document (TVML);
  • Native tvOS methods and constructors, for example, the NavigationDocument object, which controls the stack of rendered pages and is used as a history navigation.

Each of its own methods is described in the documentation for TVJS . The rest is the usual JavaScript. ECMAScript 2015 is partially supported, for example, classes and template strings.

Arrow functions, constants are not supported. At the same time, some third-party libraries may not work well. For example, I had to go through several npm packages to find a working library for AJAX requests (due to calls to window.XMLHttpRequest, which is not implemented as a window property in tvOS ) . Of course, it would be easier to write a few lines of code yourself, but at some point it became a matter of principle - to find a plugin that works out of the box, and it exists!

Debugging


You can use Safari to debug JavaScript code. We activate the “Develop” menu ( Preferences -> Advanced -> Show Develop menu ), connect the Apple TV via USB. We go into the browser and launch the application on the console, select the “Apple TV” device from the “Develop” drop-down menu, then select our application.

Hello world and launch the video


The following is a small example of what a tvOS application might look like.

First you need to implement the loading of the JavaScript file. The task is to create an object of the TVApplicationController class that provides an interface for using TVML:

Show code
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  // объект window
  window = UIWindow(frame: UIScreen.mainScreen().bounds)
  // контекст, необходимый для инициализации объекта класса TVApplicationController
  let appContollerContext = TVApplicationControllerContext()
  #if DEBUG
  // для отладки можно использовать любой хост
  let tvBaseUrl = "http://192.168.xx.xx/"
  let tvBootUrl = "\(tvBaseUrl)js/main.js"
  guard let javaScriptUrl = NSURL(string: tvBootUrl) else {
    fatalError("Unable create js application url")
  }
  // ссылка на js-файл, которому необходимо передать управление
  appContollerContext.javaScriptApplicationURL = javaScriptUrl
  // указываем хост, используемый как BASE для подгрузки ресурсов
  appContollerContext.launchOptions["BASEURL"] = tvBaseUrl;
  #else
  // в боевой версии js-файл подключается в bundle приложения
  appContollerContext.javaScriptApplicationURL = NSBundle.mainBundle().URLForResource("application", withExtension:"js")!
  #endif
  // создаем объект и передаем управление js-файлу
  appController = TVApplicationController(context:appContollerContext, window: window, delegate: self)
    return true
}


After control has passed to the JavaScript file, you can begin to implement the logic. The contents of main.js:

App.onLaunch = function (options) {
  console.log('Hello World!');
}

Here's the TVJS App method. onLauch is the entry point of the application.

Let's try to make something more complicated:

App.onLaunch = function () {
  var alertString, alertXML, parser;
  // текстовый шаблон TVML-документа
  alertString = [
    '',
      '',
        'Hello World!',
        '',
      '',
    ''
  ].join('');
  // создаем экземпляр DOMParser
  parser = new DOMParser();
  // парсим XML-документ из шаблона
  alertXML = parser.parseFromString(alertString, 'application/xml');
  // выводим XML-документ на страницу
  navigationDocument.pushDocument(alertXML);
}

This example demonstrates the DOMParser constructor, which converts a string into an XML document.

Using the TVJS navigationDocument.pushDocument () method , you can display the resulting document on the page. In this case, the new page will be written to the navigationDocument.documents array , which can be used to navigate through the history. In this case, when the application starts, the page will display the inscription “Hello world” and the “OK” button.

Before inserting an XML document on a page, you can add a custom event handler:

...
// добавляем обработчик выбора элемента (клик на пульте)
alertXML.addEventListener('select', function () {
  var player, mediaItem;
  // создаем экземпляр MediaItem
  mediaItem = new MediaItem('video', 'http://example.org/path/to.m3u8');
  // создаем экземпляр плеера
  player = new Player();
  // создаем экземпляр Playlist
  player.playlist = new Playlist();
  // добавляем mediaItem в playlist
  player.playlist.push(mediaItem);
  // делаем плеер видимым
  player.present();
  // запускаем проигрывание
  player.play();
});
...

In the resulting example, an event of user choice is hung on the element of the XML document - the button, and a video player is created and launched in the callback using the TVJS methods. Now, when you click on the "OK" button, video playback will start.

The complete sample code
App.onLaunch = function () {
  var alertString, alertXML, parser;
  // текстовый шаблон TVML-документа
  alertString = [
    '',
    '',
    'Hello World!',
    '',
    '',
    ''
  ].join('');
  // создаем экземпляр DOMParser
  parser = new DOMParser();
  // парсим XML-документ из шаблона
  alertXML = parser.parseFromString(alertString, 'application/xml');
  // добавляем обработчик выбора элемента (клик на пульте)
  alertXML.addEventListener('select', function () {
    var player, mediaItem;
    // создаем экземпляр MediaItem
    mediaItem = new MediaItem('video', 'http://example.org/path/to.m3u8');
    // создаем экземпляр плеера
    player = new Player();
    // создаем экземпляр Playlist
    player.playlist = new Playlist();
    // добавляем mediaItem в свойство playlist плеера
    player.playlist.push(mediaItem);
    // делаем плеер видимым
    player.present();
    // запускаем проигрывание
    player.play();
  });
  // выводим XML-документ на страницу
  navigationDocument.pushDocument(alertXML);
};


Of course, this is a very simple example, but it fully reflects the general principle of working with TVML and TVJS and shows that this solution is not some Brave Beast , but a technology close to the classic frontend.

Finally


In conclusion, a few words about how the Rutube application is currently implemented.

The code is written in CoffeeScript, using Browserify as a modular loader. To obtain data, XMLHttpRequest and the open Rutube API are used. For video delivery, the HLS protocol is used.

Admire on the main screen of the application


In general, I liked the development on TVML and TVJS - quickly and efficiently. Although the application is created in a standardized interface, this interface looks very impressive. This saves time on typesetting and creating visual effects, and does not have to think about their performance.

Of the minuses, I would like to note the limitations in design. Well, the implementation of the search (text input) seemed ambiguous. We don’t even know if it is worth adding it to the application without Siri?

The application was written in a few days, but if you are inspired and start developing your own, put some time into the evaluation for mastering unfamiliar APIs.

In the future, the release of the plugin for React is expected , which so far (November 2015) is in the status of "very alpha".

In the meantime, it should be noted that over the past week since the release, without straining, “over a cup of tea”, we have added a number of “nice little things” to our application and are preparing an update.

useful links


TVMLKit
Documentation TVJS Documentation TVML
Documentation
Beginning tvOS Development with TVML Tutorial

Also popular now: