TVMLで実装したtvOSアプリのJavaScriptキャッシュについて

はじめに

はじめまして。株式会社ビデオマーケットでサーバサイドエンジニアを担当しております、yamakzと申します。

弊社のtvOSアプリはTVMLKitで実装しております。 TVMLKitとはTVMLファイルとJavaScript環境を介してUIを構築できます。 具体的な方法としては、アプリにTVMLKitを組み込むことによってTVMLKitとサーバ上のTVMLKit JSが通信し、 TVMLとJavaScriptによってUIが生成されます。 多くのことがサーバ側で行えるため、利点も多いですが、ネイティブアプリとは違った注意点もあります。 今回はJavaScriptのキャッシュで不都合が生じましたので、その要因と解決法について記載いたします。

TVML及びTVMLKit JSを利用した実装の利点

  1. サーバ側で更新がほぼ完結するので、アプリの審査が不要
  2. TVMLには様々なテンプレートが用意されており、用途に応じて適用できる
  3. TVMLとJavaScriptなので、Swiftの知識がなくても表示から画面遷移まで実装できる

このように、一部アプリ側の機能を使用する部分(IAP決済等)を除き、サーバサイドで更新が完結するため、 アプリ審査へ提出せず画面を改修できるのが利点だと思います。 また、TVMLはHTMLに近いのでフロントエンドエンジニアの方も理解が容易です。

JavaScriptキャッシュの問題

サーバ側で更新が完結するTVMLですが、難しい点もあります。 その一つがJavaScriptのキャッシュの問題です。 アプリ内にキャッシュが残ってしまい、サーバ側の更新がアプリで適用されていないことがあります。 ブラウザと違い、簡単にキャッシュクリアというわけにはいきません。 アプリの再インストールで解決しますが、これをすると上記利点がなくりますので、再インストールをせずに解消する方法を模索しました。

TVMLKitでの実装方法

まず、TVMLで実装する方法を簡単に記載します。

1.JavaScriptファイルを設置する

任意の場所に起動時の処理を記載したJavaScriptファイルを配置します。

App.onLaunch = function() {
    // 起動時の処理を記載
}

参考 developer.apple.com

2.各ページの処理を記載したJavaScriptファイルを読み込む

起動時に各ページの動作を記載したJavaScriptファイルを読み込みます。

App.onLaunch = function(options) {
    let baseURL = options.baseURL;
    const javascriptFiles = [
        "TitleController",
        "UserController"
    ].map(
        moduleName => `${baseURL}js/${moduleName}.js`
    );

    // 各JavaScriptを読み込む
    evaluateScripts(javascriptFiles, async function(scriptsAreLoaded) {
    }
}

参考 developer.apple.com

3.アプリ側でURLを指定する

下記のようにアプリ側で利用するJavaScriptのURLを指定します。

let appControllerContext = TVApplicationControllerContext()
appControllerContext.javaScriptApplicationURL = {$javaScriptURL}

参考 developer.apple.com

キャッシュが残る状況

1.のJavaScriptファイルは起動時に再読み込みされるようで、こちらの更新は即時反映されます。 しかし、2.のようにevaluateScriptsから読み込んだ別ファイルのJavaScriptの場合、読み込み先ファイルのキャッシュが残り、更新されませんでした。

対応方法

3.でJavaScriptのURLを指定する際に下記のようにダミーのtimestampパラメータを付与することで常に最新のファイルが読み込まれました。

let appControllerContext = TVApplicationControllerContext()
appControllerContext.javaScriptApplicationURL = {$javaScriptURL} + "/?timestamp=\(Date().timeIntervalSince1970)"

終わりに

TVMLは情報が少なく、実際に動作させないと挙動が見えづらい部分もありますが、 JavaScriptでアプリケーションが作成できるのはとても良い点だと思います。 今回のキャッシュの件も含め、TVMLKit JS独特の挙動が発生する場合があるので、自分が取り組む中で気がついた点については またの機会に記載したいと思います。 TVMLKitで作成されるアプリケーションが少しでも増える助けになれば幸いです。

参考

公式のドキュメントの他、以下も参考にいたしました。 https://developer.apple.com/forums/thread/80749