目次

はじめに

想定環境
免責事項
表記関係について
底本について

1. JavaScriptCore

1.1. JavaScriptCore
1.2. JavaScriptCoreで出来ること
1.3. JavaScript
1.4. ECMAScript対応状況
1.5. SwiftでJavaScript始めてみませんか?
1.6. Playgroundでも試せる

2. 基本的な使い方

2.1. 実装の流れ
2.2. JavaScriptCoreを使えるようにする
2.3. コンテキストを生成する
2.4. スクリプトを実行する
2.5. スクリプトを実行した結果を取得する
2.6. 変数の値を取得する
2.7. ここまでのまとめ

3. 変数の扱い方

3.1. 変数を表現する型
3.2. JSValue型から値を取得する
3.3. どの型に変換できるかを調べる
3.4. JavaScriptオブジェクトを操作する
3.5. JSValue型の値を比較する
3.6. そのほかの型の扱いについて

4. APIを定義する

4.1. 用意されているAPI
4.2. APIの定義方法
4.3. 変数をJavaScriptで定義する
4.4. 変数をSwiftから提供する
4.5. 関数をJavaScriptで定義する
4.6. 関数をSwiftから取り込んで提供する
4.7. オブジェクトをJavaScriptで定義する
4.8. Swiftの型をJavaScriptに直接提供する
4.9. SwiftクラスをJavaScriptから生成する

5. 変数を扱うときの注意

5.1. プリミティブ型
5.2. 文字列への変換
5.3. 真偽値の表現
5.4. 真偽値への変換
5.5. 桁あふれを伴う数値変換
5.6. 数値オブジェクトへの変換
5.7. Double型への変換
5.8. ネイティブオブジェクトを取得する
5.9. 配列型への変換
5.10. 辞書型への変換
5.11. オブジェクトの扱いに注意
5.12. 実行結果の取得失敗談
5.13. ネイティブ型に変換できなかったとき
5.14. nullを変換したとき
5.15. undefinedを変換したとき

6. 座標や範囲を表す型の扱い

6.1. JavaScriptオブジェクトとの相互変換
6.2. Swiftネイティブオブジェクトでの型
6.3. JavaScriptオブジェクトでの型
6.4. JavaScript側でNSValue型を扱う方法も
6.5. JSValue型にラップして渡すのが自然
6.6. JavaScriptで作ってSwiftで取得するとき
6.7. JavaScriptでCGSize型を作る

7. 例外処理を使う

7.1. 例外を検出する
7.2. 例外の詳細を取得する
7.3. 標準的なエラーオブジェクト
7.4. JavaScriptから例外を送信する
7.5. Swiftから例外を送信する

8. SwiftでPromiseを使う

8.1. 非同期処理
8.2. コールバック関数で非同期処理を行ってみる
8.3. Promiseオブジェクトを生成する
8.4. 続きの処理を実行する
8.5. 続けてさらに処理を行う
8.6. コードの見通しを良くするには
8.7. エラーが発生した場合に対処する
8.8. 途中で値が決まらないことが確定したとき
8.9. 必ず実行したい処理があるとき

9. コードを書きやすくする

9.1. JavaScriptをファイルから読み込む
9.2. CGSize型などを安全に扱う
9.3. キーを文字列リテラルで指定する
9.4. JSValueの比較をJavaScriptと同じにする
9.5. JSValueを大小比較する
9.6. 添字構文でオブジェクトを設定する
9.7. JavaScriptコードをreturnで終わらせる
9.8. setTimeout関数を使う

10. Promiseを使いやすくする

10.1. Promiseクラスは用意しない
10.2. JSValue型にthenメソッドを用意する
10.3. thenメソッドでエラーを扱う
10.4. catchメソッドを用意する
10.5. finallyメソッドを用意する
10.6. Promiseオブジェクトを作りやすくする

あとがき

キーワード索引

逆引き索引

はじめに

 iOSアプリやmacOSアプリの開発では、標準で「JavaScriptCoreフレームワーク」というJavaScript実行環境を利用できます。

 これを使うと、JavaScriptコードを文字列として実行するだけでなく、SwiftのネイティブなコードとJavaScriptのコードとを密接に連携して、インスタンスを介して互いのデータにアクセスするなど、自由度の高いプログラミングが可能になります。

 本書では、そんなJavaScriptCoreフレームワークをSwiftで扱う方法を、順を追って見ていきます。iOS アプリやmacOSアプリでネイティブなJavaScriptプログラミングを始めてみましょう。

想定環境

 ・Swift 5.2.2

 ・Xcode 11.4.1 (11E503a)

免責事項

 本書の内容は、情報提供のみを目的としております。正確性には留意しておりますが、正確性を保証するものではありません。本書の記載内容に基づく一切の結果について、筆者は一切の責任を負いません。

表記関係について

 本書に記載されている会社名、製品名などは、一般に各社の登録商標または商標、商品名です。会社名、製品名については、本文中では©︎、®︎、™️マークなどは表示していません。

底本について

 本書籍は、技術系同人誌即売会「技術書典」などで頒布されたものを底本としています。

1. JavaScriptCore

 JavaScriptCoreとは何者なのか、まずはそんなあたりから見ていきます。さらりと読んでJavaScriptCoreと、それを取り巻くJavaScriptのイメージを膨らませてみましょう。

1.1. JavaScriptCore

 JavaScriptCoreは、JavaScriptをアプリ内で実行可能にするフレームワークです。iOS 7.0以上、macOS 10.5以上、tvOS 9.0以上、Mac Catalyst 13.0以上で使えます。

 JavaScriptCoreは、実際にAppleのHTMLレンダリングエンジンWebKitのJavaScript実行エンジンとして使用され、そのWebKitはApple標準のWebブラウザーSafariで使われているそうです。そんな本格的なJavaScript実行エンジンを自作アプリに組み込んで簡単に使えることが、JavaScriptCoreの大きな醍醐味と言えます。

1.2. JavaScriptCoreで出来ること

 JavaScriptCoreを使えば、アプリ内部にJavaScript仮想マシンのインスタンスを作成し、そこで任意のJavaScriptコードを実行できるようになります。コードは文字列で与えれば良いので、実行したいJavaScriptコードをあらかじめアプリ内に組み込んでおくだけでなく、ランタイムでJavaScriptコードを作って実行することも可能です。

 Objective-CやSwiftで作ったネイティブなクラスをJavaScript仮想マシンにエクスポートして、JavaScriptコードから直接操作することもできます。インスタンスはネイティブ環境とJavaScript環境とで相互に利用できます。そのため、単にJavaScriptコードを実行して結果を得るだけでなく、JavaScriptでネイティブコードを制御することも可能になります。

1.2.1. 利用できそうな場面

 そんなJavaScriptCoreですが、できることの幅が広く強力すぎて、どんなことに使えそうかが想像しにくいところです。そこで、どんな場面で利用できそうか少し考えてみることにします。

ビルド後にアプリの挙動を修正する

 まず、思いつくのが「外部からテキストデータをダウンロードして、アプリの動作を変更したい」場面です。たとえばWebサイトをスクレイピングしてデータを取得するアプリで、参照先のWebサイトの仕様が変わったとします。このとき、アプリをアップデートすることなく迅速に対応したい場合に、有意義に活用できる仕組みのように思います。

カスタムスクリプト環境を提供する

 また、アプリ利用者が「カスタムスクリプトを書いて、動作をカスタマイズできる環境」を簡単に提供できます。SwiftネイティブコードをJavaScriptで直接制御できる1ため、必要なAPIさえ用意してあげれば、それだけでJavaScriptからのアプリの制御が可能になります。

 JavaScriptを書ける人はたくさんいると思うので、それをカスタムスクリプト用の言語として提供できるのは嬉しいところです。

1.3. JavaScript

 ところで、今回のテーマであるJavaScriptCoreで実行可能なプログラム言語JavaScriptとはどんなものか、簡単に見ておきましょう。

 個人的にJavaScriptは、主にWebブラウザーでHTMLを自由自在に扱うための言語という印象があります。そもそもは1995年頃からDynamic HTMLを実現するためのスクリプト言語として登場しました。それから今日までずっと、Webの世界を引率してきたと言っても良いかもしれません。

1.3.1. コードの扱い心地

 そんなJavaScriptですが、コードの雰囲気はイマドキの言語に引けを取らないくらいスマートな記述ができるように思います。テキストエディターでサラサラと書いて、それをコンパイル作業をしなくてもすぐに実行できる手軽さも魅力のひとつと言えそうです。

var outputItems = new Array();

var lines = clip.content.split('\n');
var expression = /^\s*([^:]*)\s*:\s*(.*)\s*$/;

for (var index in lines)
{
    var line = lines[index];
    var itemContent;

    if (line.match(expression))
    {
        itemContent = RegExp.$2;
    }
    else
    {
        itemContent = line.trim();
    }

    outputItems.push(escapeHtml(itemContent));
}

1.4. ECMAScript対応状況

 JavaScriptの言語仕様は、ECMAScriptとして標準化が図られています。策定された仕様のどこまで対応しているかは、実行環境によって違ってきます。そんな対応状況は、ECMAScript互換テーブル2のサイトで確認できました。

 たとえば2016年12月に確認したときには、Safari 10が搭載されたmacOS 10.12 SierraでECMAScript 6に完全対応しているようです。それに対してSafari 9では、ECMAScript 6には50%ほどしか対応していないものの、ECMAScript 5についてはほとんどが使えるようでした。

 そして2020年2月の時点でのSafariのECMAScript 6対応状況ですが、Safari 13が搭載されたmacOS 10.15 Catalinaでは、下がって99%の対応状況になっていました。それ以降のECMAScript 2016+ 対応状況としては、Safari 13で80%の対応状況です。そのひとつ前のSafari 12.1では75%の対応状況になっているようです。

1.4.1. 実行環境による違いも

 また、JavaScriptCoreを使う場合は、実行時のOSのバージョンによって使える機能も変わってきます。最新のJavaScript機能を使いたいときには、どのOSでどのバージョンJavaScriptCoreが使えるのかをまず把握して、適切な環境だけで動作するように、アプリのDeployment Targetを設定する必要がありそうです。

1.5. SwiftでJavaScript始めてみませんか?

 JavaScriptCoreは、強力で魅力的なフレームワークになっていると思います。可能性は無限大、そんなJavaScriptCoreを使ってみませんか?

 以降では、Swiftを使ってJavaScriptCoreを扱う方法を綴っていきます。それを見れば、その強力さとは裏腹に、シンプルな記述で使えることを知ってもらえるかと思います。ぜひ真似して使ってみて、そこから新しいプログラミングの世界を拓くきっかけにしてもらえたら幸いです。

1.6. Playgroundでも試せる

 JavaScriptCoreは、Playgroundで実行できるようになっています。MacにXcode3を入れればすぐに試せるので、ぜひ遊んでみてください。ちょっとしたJavaScriptコードの動作を試したいときにも便利です。

 PlaygroundでJavaScriptCoreを使う際、気をつけておきたい点があります。SwiftのクラスをJavaScript側で使いたい場面では、少なくともJavaScriptにエクスポートするAPIを記載するプロトコルの定義は、即時に実行されるエディターではなく、そのサブフォルダーSources内のSwiftファイルに書く必要があります。これについては <4.8> で紹介します。

1. JavaScriptにSwiftで記述したAPIを提供する方法は <4.8> で紹介します。

2. http://kangax.github.io/compat-table/es6/

3. https://itunes.apple.com/jp/app/xcode/id497799835?mt=12

試し読みはここまでです。
この続きは、製品版でお楽しみください。