Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
JavaScriptCore
framework	
EZ-‐‑‒NET  熊⾕谷友宏    @es_̲kumagai
http://program.station.ez-‐‑‒net.jp/
の普通な使い⽅方
⾃自⼰己紹介
@es_̲kumagai	
EZ-‐‑‒NET  IP  Phone	
 ⾳音で再配達ゴッド	
⾳音で再配達	
 ⾳音でダイヤル	
 いつもの電卓
for  iPad	
いつもの電卓
for  iPhone	
EZ-‐‑‒NET ...
書籍
こんなご時世ですが
ぜひ⼿手に取ってパラパラめくってみてください。
•  Xcode  全機能を網羅羅
•  プロジェクトの作り⽅方
•  ソースコード編集の効率率率化
•  ショートカットキーの紹介
•  オートレイアウトの使い⽅方
•...
JavaScriptCore.framework
特徴
JavaScript  ⾔言語
1.  Web  でお馴染みのスクリプト⾔言語
2.  ⼿手軽にコードを組み⽴立立てられる
3.  JavaScript  を使える⼈人は多いはず
特徴
JavaScriptCore.framework
1.  アプリ内で  JavaScript  を実⾏行行可能
2.  ネイティブコードとの相互運⽤用が可能
OS  X  10.9、iOS  7.0  から利利⽤用可能
特徴
1.  アプリ内で  JavaScript  を実⾏行行可能
1.  スクリプトをテキストで⽤用意する
2.  実⾏行行する直前までに⽤用意すれば良良い
3.  ビルドに依らない実装が可能になる
    ➡  コードを⾃自由に差し替えられ...
特徴
2.  ネイティブコードとの相互運⽤用
1.  変数の値を⾃自由に受け渡しできる
➡  ネイティブの⾃自作クラスも交換可能
2.  JavaScript  からネイティブコードの
メソッドを実⾏行行できる
3.  ネイティブコードから
J...
JavaScriptCore.framework
実⾏行行の流流れ
実⾏行行⼿手順  (1/4)
JavaScriptCore  をインポート
import JavaScriptCore
ターゲット設定の  Linked  Frameworks  and  Libraries  で
JavaScriptCore...
実⾏行行⼿手順  (2/4)
実⾏行行環境のコンテキストを⽣生成  
let context = JSContext()
このコンテキスト内で  JavaScript  を実⾏行行する
実⾏行行⼿手順  (3/4)
JavaScript  コードを実⾏行行
let script = "var value = encodeURI('<name>');"
context.evaluateScript(script)
実⾏行行結果は...
実⾏行行⼿手順  (4/4)
コンテキストから値を取得
let value:JSValue =
context.objectForKeyedSubscript("value")
println(value.toString())
コンテキスト内...
JavaScriptCore.framework
実⾏行行⽅方法の詳細
実⾏行行⽅方法の詳細
JavaScript  コードを実⾏行行
JavaScript  コードを実⾏行行
JavaScript  コードの実⾏行行
context.evaluateScript(script) -> JSValue!
•  実⾏行行したい  JavaScript  を⽂文字列列で渡す
•  ...
JavaScript  コードを実⾏行行
JavaScript  コンテキストから変数を取得
context.objectForKeyedSubscript(name)
-> JSValue!
•  取得したい変数名を⽂文字列列で渡す
•  変...
JavaScript  コードを実⾏行行
•  toInt32() ->Int32
•  toUint32() ->Uint32
•  toDouble() ->Double
•  toString() ->String!
•  toBool(...
JavaScript  コードを実⾏行行
JSValue  の未定義値をネイティブな値に変換
undefined
•  toString()
•  toInt32()
•  toDouble()
•  toBool()
•  toObject(...
JavaScript  コードを実⾏行行
JSValue  の  null  値をネイティブな値に変換
null
•  toString()
•  toInt32()
•  toDouble()
•  toBool()
•  toObject(...
JavaScript  コードを実⾏行行
•  isNumber()
•  isString()
•  isBoolean()
•  isObject()
•  isNull()
•  isUndefined()
JSValue  の型を判定する
実⾏行行⽅方法の詳細
JavaScript  に直接
変数を登録
JavaScript  に直接変数を登録
JavaScript  コンテキストに値を登録
context.setObject(value,forKeyedSubscript:name)
-> Void
•  設定したい変数名を⽂文字列列で渡す ...
JavaScript  に直接変数を登録
変数にネイティブオブジェクトも登録可能
詳細は後ほど
実⾏行行⽅方法の詳細
JavaScript  に直接
関数を登録
JavaScript  に直接関数を登録
Objective-‐‑‒C  で関数を登録する場合
context[@"sum"] = ^(NSArray* values)
{
NSInteger result = 0;
for (NSNumber...
JavaScript  に直接関数を登録
Swift  で関数を登録する場合  (1/6)
context.setObject(value,forKeyedSubscript:name)
-> Void
•  JSContext  は  Obj...
JavaScript  に直接関数を登録
Swift  で関数を登録する場合  (2/6)
引数と戻り値が明⽰示的な  Blocks  引数には
Swift  のクロージャを渡せる
➡  JSContext  を  Objective-‐‑‒C...
JavaScript  に直接関数を登録
Swift  で関数を登録する場合  (3/6)
#import <JavaScriptCore/JavaScriptCore.h>
typedef id (^unaryFunction)(id);
t...
JavaScript  に直接関数を登録
Swift  で関数を登録する場合  (4/6)
- (void)setUnaryFunction:(unaryFunction)function
forKeyedSubscript:(NSString...
JavaScript  に直接関数を登録
Swift  で関数を登録する場合  (5/6)
#import "JSContext+Closure.h"
$(PROJECT_̲NAME)-‐‑‒Bridging-‐‑‒Header.h
このヘッダ...
JavaScript  に直接関数を登録
Swift  で関数を登録する場合  (6/6)
let function = { (values:AnyObject!)->AnyObject in
var sum:Int = 0
for value...
JavaScript  に直接関数を登録
登録した関数は  JavaScript  で普通に利利⽤用可能
context.evaluateScript("sum([10,20,30]);")
•  実⾏行行⽅方法は通常の  JavaScript...
実⾏行行⽅方法の詳細
複数⾏行行に渡る
JavaScript  コードの実⾏行行
複数⾏行行に渡る  JavaScript  コードの実⾏行行
Case  1:
ひとつの⽂文字列列にまとめて実⾏行行  #1
context.evaluateScript(
"var tag='<name>';n var val=encodeU...
複数⾏行行に渡る  JavaScript  コードの実⾏行行
Case  2:
ひとつの⽂文字列列にまとめて実⾏行行  #2
context.evaluateScript(
"var tag='<name>'; var val=nencodeU...
複数⾏行行に渡る  JavaScript  コードの実⾏行行
Case  3:
各⾏行行を複数回に分けて実⾏行行  #1
context.evaluateScript(
"var tag='<name>';")
context.evaluate...
複数⾏行行に渡る  JavaScript  コードの実⾏行行
Case  4:
各⾏行行を複数回に分けて実⾏行行  #2
context.evaluateScript(
"var tag='<name>'; var val=")
context...
複数⾏行行に渡る  JavaScript  コードの実⾏行行
Case  5:
各⾏行行を複数回に分けて実⾏行行  #3
context.evaluateScript("if (value==1)")
context.evaluateScrip...
複数⾏行行に渡る  JavaScript  コードの実⾏行行
Case  6:
各⾏行行を複数回に分けて実⾏行行  #4
context.evaluateScript("try {")
context.evaluateScript("value...
複数⾏行行に渡る  JavaScript  コードの実⾏行行
複数⾏行行の  JavaScript  は
意味的に不不⾜足のない単位で
実⾏行行すること
実⾏行行⽅方法の詳細
JavaScript  の
実⾏行行時エラーを検出
JavaScript  の実⾏行行時エラーを検出
evaluateScript  でエラーが発⽣生すると…
JavaScript  内で
例例外エラーが発⽣生する
JavaScript  の実⾏行行時エラーを検出
⼀一般的な  JavaScript  例例外オブジェクト
•  Error
•  SyntaxError
•  TypeError
•  EvalError
•  RangeError
•  R...
JavaScript  の実⾏行行時エラーを検出
JavaScript  内で発⽣生した例例外は
ネイティブコードで検出可能
•  コンテキストに  exceptionHandler  を登録
•  例例外が発⽣生すると関数が呼び出される
JavaScript  の実⾏行行時エラーを検出
JavaScript  例例外をネイティブコードで検出する
context.ex...
•  context:   実⾏行行した  JSContext  を取得
•  exception: 例例外オブジェクトを取得
JavaScript  例例外をネイティブコードで検出する
exceptionHandler  を登録
contex...
JavaScript  例例外をネイティブコードで検出する
exception  から詳細情報を取得
•  .toString()
–  エラーメッセージ  を取得
–  "SyntaxError:  Expected  token  ':'"...
JavaScript  の実⾏行行時エラーを検出
exception  オブジェクトは
例例外  Error  オブジェクトそのもの
•  JavaScript  から  Error  例例外を送出
•  exceptionHandler  で受け取れる
•  エラーメッセージは  "Error:  message"
JavaScript  の実⾏行行時エラーを検出
Java...
•  独⾃自名の例例外を送出
•  エラーメッセージは  "MyError:  message"
JavaScript  の実⾏行行時エラーを検出
カスタムエラーの名称を指定可能
context.evaluateScript(
"var err...
JavaScriptCore.framework
ネイティブオブジェクトの利利⽤用
ネイティブオブジェクトの利利⽤用
利利⽤用⽅方法
1.  JavaScript  で使える機能を宣⾔言
2.  ネイティブオブジェクトを⽣生成
3.  JavaScript  からプロパティを参照
4.  JavaScript  からメソッドを...
ネイティブオブジェクトの利利⽤用
JavaScript  で
使える機能を宣⾔言
JavaScript  で使える機能を宣⾔言
JSExport  を継承したプロトコルを作成
import JavaScriptCore
@objc protocol EZObjectJSExport: JSExport
{
var name:...
JavaScript  で使える機能を宣⾔言
先ほどのプロトコルを継承したクラスを実装
public class EZObject: NSObject, EZObjectJSExport
{
public override init() { …...
JavaScript  で使える機能を宣⾔言
オブジェクトの定義完了了
•  未定義のメソッドを呼び出すと
"TypeError:  'undefined'  is  not  a  function"
•  未定義のプロパティを呼び出すと  "...
ネイティブオブジェクトの利利⽤用
ネイティブオブジェクトの
インスタンスを作る
1.  ネイティブコードから⽣生成する⽅方法
2.  JavaScript  内で⽣生成する⽅方法
•  ネイティブコードで⽣生成したインスタンスを
コンテキストの  変数にそのまま登録
•  JavaScript  内から変数をとおして利利⽤用可能
ネイティブオブジェクトのインスタンスを作る
ネイティブコードから⽣生成する⽅方法
let o...
@objc protocol EZObjectJSExport: JSExport
{
class func create()->AnyObject
}
public class EZObject: NSObject, EZObjectJSEx...
•  クラス情報をコンテキストに登録
•  クラスメソッドを使ってインスタンス⽣生成
ネイティブオブジェクトのインスタンスを作る
JavaScript  内で⽣生成する⽅方法(実装)
context.setObject(EZObject.sel...
•  ネイティブコードへの取り出しも可能
ネイティブオブジェクトのインスタンスを作る
JavaScript  内で⽣生成する⽅方法(余談)
let object = context.objectForKeyedSubscript("obj")
...
ネイティブオブジェクトの利利⽤用
JavaScript  から
プロパティを使⽤用
•  プロパティ名の後には括弧不不要
•  JavaScript  どおりの⽅方法で読み書き可能
JavaScript  からプロパティを使⽤用
ネイティブコードのプロパティを読み書き
context.evaluateScript("var n...
ネイティブオブジェクトの利利⽤用
JavaScript  から
メソッドを実⾏行行
•  JavaScript  どおりの⽅方法で実⾏行行可能
•  引数を取らないメソッドも括弧が必要
JavaScript  からメソッドを実⾏行行
ネイティブコードのメソッドを実⾏行行
context.evaluateScript("obj....
ネイティブオブジェクトの利利⽤用
メソッド実装時の注意
JavaScript  からメソッドを実⾏行行
メソッド実装時の注意  #1
•  JavaScript  組み込みの  toString()  が優先
•  独⾃自に実装しても呼び出されない  (Beta  5)
toString()  メソ...
JavaScript  からメソッドを実⾏行行
メソッド実装時の注意  #2
Swift  のメソッドは
引数のラベルが反映された名称になる
func set(name:String, value:String)->Void
➡ void se...
JavaScriptCore.framework
相互運⽤用
JavaScript  とネイティブコード
JavaScript  とネイティブコードの相互運⽤用
JavaScript  関数を
ネイティブコードで実⾏行行
•  取得時は引数を添えずに関数名を指定する
–  JavaScript  関数の参照  を取得可能
•  実⾏行行時は引数を配列列で渡す
–  複数の引数を渡せる
–  今回は配列列を取る関数なので配列列を配列列に⼊入れている
JavaScr...
JavaScript  とネイティブコードの相互運⽤用
JavaScript  オブジェクトと
ネイティブオブジェクトの相互運⽤用
オブジェクトの相互運⽤用
おさらい
ネイティブオブジェクトを  JavaScript  に取り込む場合
JavaScript  からネイティブオブジェクトを取得する場合
let object = EZObject()
context.setOb...
オブジェクトの相互運⽤用
どちらとも
相互にオブジェクトを操作可能
•  JavaScript  で設定した値を
直ぐに  ネイティブオブジェクトから利利⽤用可能
オブジェクトの相互運⽤用
JavaScript  での変更更がネイティブコードに反映
context.evaluateScript("obj.val...
•  ネイティブオブジェクトで設定した値を
直ぐに  JavaScript  から利利⽤用可能
オブジェクトの相互運⽤用
ネイティブコードでの変更更が  JavaScript  に反映
object.value = "FromNative"
c...
JavaScriptCore.framework
便便利利な使い⽅方
おまけ
便便利利な使い⽅方
スクリプトを
ファイルから読み込んで実⾏行行
var bundle = NSBundle.mainBundle()
var path = bundle.pathForResource("Script",ofType:"js")
let script = NSString(contentsO...
function sum(array)
{
var result = 0;
for (var i = 0; i < array.length; ++i)
{
result += array[i];
}
return result;
}
var ...
便便利利な使い⽅方
return  命令令で
終われるスクリプトにする
function sum(array)
{
var result = 0;
for (var i = 0; i < array.length; ++i)
{
result += array[i];
}
return result;
}
retu...
オブジェクトの相互運⽤用
SyntaxError:
Return  statements  are
only  valid  inside  functions
そのまま使うと…
return  命令令は関数内で使わなければいけない
return  命令令で終われるスクリプトにする
スクリプトを実⾏行行時に匿匿名関数で包む
let result =
context.evaluateScript("(function(){(script)})();")
•  スクリプトを関数...
JavaScriptCore.framework
•  JavaScript  は⼿手軽に使えるスクリプト⾔言語
•  JavaScript  コードをアプリ内で簡単に実⾏行行
•  ネイティブオブジェクトとの相互運⽤用が可能
•  JavaS...
Upcoming SlideShare
Loading in …5
×

JavaScriptCore.framework の普通な使い方 #cocoa_kansai

26,171 views

Published on

Objective-C や Swift のネイティブコードから JavaScript をランタイムで実行するための JavaScriptCore.framework のお話です。基本的な機能の説明と、注意点を整理して紹介しています。

Published in: Software
  • Be the first to comment

JavaScriptCore.framework の普通な使い方 #cocoa_kansai

  1. 1. JavaScriptCore framework EZ-‐‑‒NET  熊⾕谷友宏    @es_̲kumagai http://program.station.ez-‐‑‒net.jp/ の普通な使い⽅方
  2. 2. ⾃自⼰己紹介 @es_̲kumagai EZ-‐‑‒NET  IP  Phone ⾳音で再配達ゴッド ⾳音で再配達 ⾳音でダイヤル いつもの電卓 for  iPad いつもの電卓 for  iPhone EZ-‐‑‒NET  熊⾕谷友宏   http://program.station.ez-‐‑‒net.jp/
  3. 3. 書籍 こんなご時世ですが ぜひ⼿手に取ってパラパラめくってみてください。 •  Xcode  全機能を網羅羅 •  プロジェクトの作り⽅方 •  ソースコード編集の効率率率化 •  ショートカットキーの紹介 •  オートレイアウトの使い⽅方 •  ローカライズの設定⽅方法 •  バージョン管理理の使い⽅方 •  ビルド設定とスキーム設定 •  ほか、とにかくいろいろ 特設サイト  ̶—  http://ez-‐‑‒net.jp/sp/xcode5/
  4. 4. JavaScriptCore.framework
  5. 5. 特徴 JavaScript  ⾔言語 1.  Web  でお馴染みのスクリプト⾔言語 2.  ⼿手軽にコードを組み⽴立立てられる 3.  JavaScript  を使える⼈人は多いはず
  6. 6. 特徴 JavaScriptCore.framework 1.  アプリ内で  JavaScript  を実⾏行行可能 2.  ネイティブコードとの相互運⽤用が可能 OS  X  10.9、iOS  7.0  から利利⽤用可能
  7. 7. 特徴 1.  アプリ内で  JavaScript  を実⾏行行可能 1.  スクリプトをテキストで⽤用意する 2.  実⾏行行する直前までに⽤用意すれば良良い 3.  ビルドに依らない実装が可能になる     ➡  コードを⾃自由に差し替えられる     ➡  リソースと同じように  DL  適⽤用できる     ➡  アプリ使⽤用者にカスタムスクリプトを         書かせる機能を容易易に実現できる
  8. 8. 特徴 2.  ネイティブコードとの相互運⽤用 1.  変数の値を⾃自由に受け渡しできる ➡  ネイティブの⾃自作クラスも交換可能 2.  JavaScript  からネイティブコードの メソッドを実⾏行行できる 3.  ネイティブコードから JavaScript  の関数を実⾏行行できる
  9. 9. JavaScriptCore.framework 実⾏行行の流流れ
  10. 10. 実⾏行行⼿手順  (1/4) JavaScriptCore  をインポート import JavaScriptCore ターゲット設定の  Linked  Frameworks  and  Libraries  で JavaScriptCore.framework  をリンクしておくこと
  11. 11. 実⾏行行⼿手順  (2/4) 実⾏行行環境のコンテキストを⽣生成   let context = JSContext() このコンテキスト内で  JavaScript  を実⾏行行する
  12. 12. 実⾏行行⼿手順  (3/4) JavaScript  コードを実⾏行行 let script = "var value = encodeURI('<name>');" context.evaluateScript(script) 実⾏行行結果はコンテキスト内に蓄積される
  13. 13. 実⾏行行⼿手順  (4/4) コンテキストから値を取得 let value:JSValue = context.objectForKeyedSubscript("value") println(value.toString()) コンテキスト内の値を  JSValue  型で取得できる %3Cname%3E
  14. 14. JavaScriptCore.framework 実⾏行行⽅方法の詳細
  15. 15. 実⾏行行⽅方法の詳細 JavaScript  コードを実⾏行行
  16. 16. JavaScript  コードを実⾏行行 JavaScript  コードの実⾏行行 context.evaluateScript(script) -> JSValue! •  実⾏行行したい  JavaScript  を⽂文字列列で渡す •  最後に実⾏行行した命令令の参照を受け取れる –  最後が『value;』なら『value  の値』 –  最後が『10  +  3;』なら『13』 –  最後が『x  =  10;』なら代⼊入後の『x  の値』 –  最後が『var  x  =  10;』だと『undefined』
  17. 17. JavaScript  コードを実⾏行行 JavaScript  コンテキストから変数を取得 context.objectForKeyedSubscript(name) -> JSValue! •  取得したい変数名を⽂文字列列で渡す •  変数の値を  JSValue  型で取得 –  toXXXX()  メソッドでネイティブ型に変換可能 –  指定した型に合わせて値が変換される –  存在しない名前では  undefined  が得られる ※  Objective-‐‑‒C  なら  context[name]  で取得可能
  18. 18. JavaScript  コードを実⾏行行 •  toInt32() ->Int32 •  toUint32() ->Uint32 •  toDouble() ->Double •  toString() ->String! •  toBool() ->Bool •  toObject() ->AnyObject! •  toArray()    ->[AnyObject]! •  toDictionary() ->[NSObject:AnyObject]! •  toNumber() ->NSNumber! •  toDate() ->NSDate! •  toPoint() ->CGPoint •  toSize() ->CGSize •  toRect() ->CGRect •  toRange() ->NSRange JSValue  からネイティブ型に変換
  19. 19. JavaScript  コードを実⾏行行 JSValue  の未定義値をネイティブな値に変換 undefined •  toString() •  toInt32() •  toDouble() •  toBool() •  toObject() ➡ "undefined" ➡ 0 ➡ Double.NaN ➡ false ➡ nil
  20. 20. JavaScript  コードを実⾏行行 JSValue  の  null  値をネイティブな値に変換 null •  toString() •  toInt32() •  toDouble() •  toBool() •  toObject() ➡ "null" ➡ 0 ➡ 0.0 ➡ false ➡ nil
  21. 21. JavaScript  コードを実⾏行行 •  isNumber() •  isString() •  isBoolean() •  isObject() •  isNull() •  isUndefined() JSValue  の型を判定する
  22. 22. 実⾏行行⽅方法の詳細 JavaScript  に直接 変数を登録
  23. 23. JavaScript  に直接変数を登録 JavaScript  コンテキストに値を登録 context.setObject(value,forKeyedSubscript:name) -> Void •  設定したい変数名を⽂文字列列で渡す  (name) –  存在しない名前の場合は新規登録する –  登録済みの名前なら値を上書きする •  設定する値を渡す  (value) –  任意のネイティブ型を指定できる –  JavaScript  はもともと  Variant  型 –  内部的には  [object  number]  や  [object  string]  等で認識識 –  nil  を渡すと  [object  undefined]  が設定される
  24. 24. JavaScript  に直接変数を登録 変数にネイティブオブジェクトも登録可能 詳細は後ほど
  25. 25. 実⾏行行⽅方法の詳細 JavaScript  に直接 関数を登録
  26. 26. JavaScript  に直接関数を登録 Objective-‐‑‒C  で関数を登録する場合 context[@"sum"] = ^(NSArray* values) { NSInteger result = 0; for (NSNumber* value in values) { result += value.integerValue; } return result; }; 変数の値として  Blocks  を渡すだけ  で  OK
  27. 27. JavaScript  に直接関数を登録 Swift  で関数を登録する場合  (1/6) context.setObject(value,forKeyedSubscript:name) -> Void •  JSContext  は  Objective-‐‑‒C  クラス •  Objective-‐‑‒C  では  id  型  で指定する •  Swift  では  AnyObject!  型  で指定する •  Swift  クロージャは  AnyObject!  に渡せない
  28. 28. JavaScript  に直接関数を登録 Swift  で関数を登録する場合  (2/6) 引数と戻り値が明⽰示的な  Blocks  引数には Swift  のクロージャを渡せる ➡  JSContext  を  Objective-‐‑‒C  カテゴリ拡張して     明⽰示的な  Blocks  を受け取るメソッドを作る
  29. 29. JavaScript  に直接関数を登録 Swift  で関数を登録する場合  (3/6) #import <JavaScriptCore/JavaScriptCore.h> typedef id (^unaryFunction)(id); typedef id (^binaryFunction)(id, id); @interface JSContext (Closure) - (void)setUnaryFunction:(unaryFunction)function forKeyedSubscript:(NSString*)key; - (void)setBinaryFunction:(binaryFunction)function forKeyedSubscript:(NSString*)key; @end JSContext+Closure.h
  30. 30. JavaScript  に直接関数を登録 Swift  で関数を登録する場合  (4/6) - (void)setUnaryFunction:(unaryFunction)function forKeyedSubscript:(NSString*)key { [self setObject:function forKeyedSubscript:key]; } - (void)setBinaryFunction:(binaryFunction)function forKeyedSubscript:(NSString*)key { [self setObject:function forKeyedSubscript:key]; } JSContext+Closure.m
  31. 31. JavaScript  に直接関数を登録 Swift  で関数を登録する場合  (5/6) #import "JSContext+Closure.h" $(PROJECT_̲NAME)-‐‑‒Bridging-‐‑‒Header.h このヘッダーをブリッジヘッダーにインポートして… ブリッジヘッダーは  "Swift  Compiler  -‐‑‒  Code  Generation"  設定の “Objective-‐‑‒C  Bridging  Header”  に登録されている
  32. 32. JavaScript  に直接関数を登録 Swift  で関数を登録する場合  (6/6) let function = { (values:AnyObject!)->AnyObject in var sum:Int = 0 for value in values as NSArray { sum += value.integerValue } return sum } context.setUnaryFunction(function,forKeyedSubscript:"sum") これでクロージャを  JavaScript  へ登録可能に。
  33. 33. JavaScript  に直接関数を登録 登録した関数は  JavaScript  で普通に利利⽤用可能 context.evaluateScript("sum([10,20,30]);") •  実⾏行行⽅方法は通常の  JavaScript  のとおり •  結果の取得⽅方法は前述のとおり
  34. 34. 実⾏行行⽅方法の詳細 複数⾏行行に渡る JavaScript  コードの実⾏行行
  35. 35. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 Case  1: ひとつの⽂文字列列にまとめて実⾏行行  #1 context.evaluateScript( "var tag='<name>';n var val=encodeURI(tag);") •  改⾏行行⽂文字が含まれていても実⾏行行可能 [OK]
  36. 36. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 Case  2: ひとつの⽂文字列列にまとめて実⾏行行  #2 context.evaluateScript( "var tag='<name>'; var val=nencodeURI(tag);") •  JavaScript  として適切切であれば コードの途中に改⾏行行⽂文字を挿⼊入可能 [OK]
  37. 37. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 Case  3: 各⾏行行を複数回に分けて実⾏行行  #1 context.evaluateScript( "var tag='<name>';") context.evaluateScript( "var val=encodeURI(tag);") •  実⾏行行結果はコンテキストに蓄積される •  次の実⾏行行時に値を引き続き利利⽤用可能 [OK]
  38. 38. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 Case  4: 各⾏行行を複数回に分けて実⾏行行  #2 context.evaluateScript( "var tag='<name>'; var val=") context.evaluateScript( "encodeURI(tag);") •  ⾏行行の途中での  evaluateScript  はできない •  SyntaxError:  Unexpected  EOF  例例外エラー [NG]
  39. 39. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 Case  5: 各⾏行行を複数回に分けて実⾏行行  #3 context.evaluateScript("if (value==1)") context.evaluateScript("{ (text="Yes") }") context.evaluateScript("else") context.evaluateScript("{ (text="No") }") •  各⾏行行が独⽴立立して実⾏行行される •  条件分岐が正しく⾏行行われない •  ひとつの  evaluateScript  で実⾏行行すれば  OK [NG]
  40. 40. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 Case  6: 各⾏行行を複数回に分けて実⾏行行  #4 context.evaluateScript("try {") context.evaluateScript("value=XXXX;") context.evaluateScript("} catch (e)") context.evaluateScript(“{ value=0; }") •  各⾏行行が独⽴立立して実⾏行行される •  例例外が正しくハンドルされない •  ひとつの  evaluateScript  で実⾏行行すれば  OK [NG]
  41. 41. 複数⾏行行に渡る  JavaScript  コードの実⾏行行 複数⾏行行の  JavaScript  は 意味的に不不⾜足のない単位で 実⾏行行すること
  42. 42. 実⾏行行⽅方法の詳細 JavaScript  の 実⾏行行時エラーを検出
  43. 43. JavaScript  の実⾏行行時エラーを検出 evaluateScript  でエラーが発⽣生すると… JavaScript  内で 例例外エラーが発⽣生する
  44. 44. JavaScript  の実⾏行行時エラーを検出 ⼀一般的な  JavaScript  例例外オブジェクト •  Error •  SyntaxError •  TypeError •  EvalError •  RangeError •  ReferenceError •  URIError
  45. 45. JavaScript  の実⾏行行時エラーを検出 JavaScript  内で発⽣生した例例外は ネイティブコードで検出可能
  46. 46. •  コンテキストに  exceptionHandler  を登録 •  例例外が発⽣生すると関数が呼び出される JavaScript  の実⾏行行時エラーを検出 JavaScript  例例外をネイティブコードで検出する context.exceptionHandler :((JSContext!,JSValue!)->Void)!
  47. 47. •  context:   実⾏行行した  JSContext  を取得 •  exception: 例例外オブジェクトを取得 JavaScript  例例外をネイティブコードで検出する exceptionHandler  を登録 context.exceptionHandler = { (context:JSContext!, exception:JSValue!)->Void in println("Error: (exception.toString())") };
  48. 48. JavaScript  例例外をネイティブコードで検出する exception  から詳細情報を取得 •  .toString() –  エラーメッセージ  を取得 –  "SyntaxError:  Expected  token  ':'"  など •  .toDictionary()["line"] as? NSNumber –  エラーが発⽣生した  ⾏行行番号  を取得 –  evaluateScript  に渡した⽂文字列列内での⾏行行番号 •  .toDictionary()["stack"] as? NSString –  関数スタック  を取得する –  改⾏行行⽂文字で区切切って関数名が記録される –  構⽂文エラーなど、スタック情報がない場合は  nil
  49. 49. JavaScript  の実⾏行行時エラーを検出 exception  オブジェクトは 例例外  Error  オブジェクトそのもの
  50. 50. •  JavaScript  から  Error  例例外を送出 •  exceptionHandler  で受け取れる •  エラーメッセージは  "Error:  message" JavaScript  の実⾏行行時エラーを検出 JavaScript  からカスタムエラーを送出可能 context.evaluateScript("throw Error(message);")
  51. 51. •  独⾃自名の例例外を送出 •  エラーメッセージは  "MyError:  message" JavaScript  の実⾏行行時エラーを検出 カスタムエラーの名称を指定可能 context.evaluateScript( "var error=Error();" + "error.name='MyError';" + "error.message='message';" + "throw error;")
  52. 52. JavaScriptCore.framework ネイティブオブジェクトの利利⽤用
  53. 53. ネイティブオブジェクトの利利⽤用 利利⽤用⽅方法 1.  JavaScript  で使える機能を宣⾔言 2.  ネイティブオブジェクトを⽣生成 3.  JavaScript  からプロパティを参照 4.  JavaScript  からメソッドを実⾏行行
  54. 54. ネイティブオブジェクトの利利⽤用 JavaScript  で 使える機能を宣⾔言
  55. 55. JavaScript  で使える機能を宣⾔言 JSExport  を継承したプロトコルを作成 import JavaScriptCore @objc protocol EZObjectJSExport: JSExport { var name:String { get set } var value:String { get set } func set(name:String, _ value:String)->Void func toData()->NSData } @objc  指定⼦子を忘れないこと
  56. 56. JavaScript  で使える機能を宣⾔言 先ほどのプロトコルを継承したクラスを実装 public class EZObject: NSObject, EZObjectJSExport { public override init() { … } public var name:String { … } public var value:String { … } public func set(name:String, _ value:String)->Void { … } public func toData()->NSData { … } } 必ず  NSObject  を継承すること
  57. 57. JavaScript  で使える機能を宣⾔言 オブジェクトの定義完了了 •  未定義のメソッドを呼び出すと "TypeError:  'undefined'  is  not  a  function" •  未定義のプロパティを呼び出すと  "undefined" JSExport  を継承したプロトコル内で定義した 機能だけを  JavaScript  から  直接  利利⽤用できる この辺りの挙動は  JavaScript  で「存在しないもの」を扱うのと同じ
  58. 58. ネイティブオブジェクトの利利⽤用 ネイティブオブジェクトの インスタンスを作る 1.  ネイティブコードから⽣生成する⽅方法 2.  JavaScript  内で⽣生成する⽅方法
  59. 59. •  ネイティブコードで⽣生成したインスタンスを コンテキストの  変数にそのまま登録 •  JavaScript  内から変数をとおして利利⽤用可能 ネイティブオブジェクトのインスタンスを作る ネイティブコードから⽣生成する⽅方法 let object = EZObject() context.setObject(object, forKeyedSubscript:"obj")
  60. 60. @objc protocol EZObjectJSExport: JSExport { class func create()->AnyObject } public class EZObject: NSObject, EZObjectJSExport { public class func create() -> AnyObject { return EZObject(); } } ネイティブオブジェクトのインスタンスを作る JavaScript  内で⽣生成する⽅方法(追加準備) インスタンスを⽣生成するクラスメソッドを追加
  61. 61. •  クラス情報をコンテキストに登録 •  クラスメソッドを使ってインスタンス⽣生成 ネイティブオブジェクトのインスタンスを作る JavaScript  内で⽣生成する⽅方法(実装) context.setObject(EZObject.self, forKeyedSubscript:"EZObject") context.evaluateScript("var obj=EZObject.create();")
  62. 62. •  ネイティブコードへの取り出しも可能 ネイティブオブジェクトのインスタンスを作る JavaScript  内で⽣生成する⽅方法(余談) let object = context.objectForKeyedSubscript("obj") .toObject() as EZObject
  63. 63. ネイティブオブジェクトの利利⽤用 JavaScript  から プロパティを使⽤用
  64. 64. •  プロパティ名の後には括弧不不要 •  JavaScript  どおりの⽅方法で読み書き可能 JavaScript  からプロパティを使⽤用 ネイティブコードのプロパティを読み書き context.evaluateScript("var name = obj.name;") context.evaluateScript("obj.value = 'NewValue';") 括弧をつけると  "Type  Error:  'PROP'  is  not  a  function"  エラー
  65. 65. ネイティブオブジェクトの利利⽤用 JavaScript  から メソッドを実⾏行行
  66. 66. •  JavaScript  どおりの⽅方法で実⾏行行可能 •  引数を取らないメソッドも括弧が必要 JavaScript  からメソッドを実⾏行行 ネイティブコードのメソッドを実⾏行行 context.evaluateScript("obj.set('NewName','NewValue');") context.evaluateScript("var data = obj.toData();") 括弧をつけないとメソッドそのものが得られる
  67. 67. ネイティブオブジェクトの利利⽤用 メソッド実装時の注意
  68. 68. JavaScript  からメソッドを実⾏行行 メソッド実装時の注意  #1 •  JavaScript  組み込みの  toString()  が優先 •  独⾃自に実装しても呼び出されない  (Beta  5) toString()  メソッドは実装しない
  69. 69. JavaScript  からメソッドを実⾏行行 メソッド実装時の注意  #2 Swift  のメソッドは 引数のラベルが反映された名称になる func set(name:String, value:String)->Void ➡ void setValue(name, value) func set(#name:String, value:String)->Void ➡ void setWithNameValue(name, value) func set(name:String, _ value:String)->Void ➡ void set(name, value)
  70. 70. JavaScriptCore.framework 相互運⽤用 JavaScript  とネイティブコード
  71. 71. JavaScript  とネイティブコードの相互運⽤用 JavaScript  関数を ネイティブコードで実⾏行行
  72. 72. •  取得時は引数を添えずに関数名を指定する –  JavaScript  関数の参照  を取得可能 •  実⾏行行時は引数を配列列で渡す –  複数の引数を渡せる –  今回は配列列を取る関数なので配列列を配列列に⼊入れている JavaScript  関数をネイティブコードで実⾏行行 JavaScript  関数の取得と実⾏行行 context.evaluateScript("function sum(array) { … }") let sum = context.objectForKeyedSubscript("sum") let result = sum.callWithArguments([ [1,3,5,7] ])
  73. 73. JavaScript  とネイティブコードの相互運⽤用 JavaScript  オブジェクトと ネイティブオブジェクトの相互運⽤用
  74. 74. オブジェクトの相互運⽤用 おさらい ネイティブオブジェクトを  JavaScript  に取り込む場合 JavaScript  からネイティブオブジェクトを取得する場合 let object = EZObject() context.setObject(object, forKeyedSubscript:"obj") context.setObject(EZObject.self, forKeyedSubscript:"EZObject") context.evaluateScript("var obj=EZObject.create();") let object = context.objectForKeyedSubscript("obj")
  75. 75. オブジェクトの相互運⽤用 どちらとも 相互にオブジェクトを操作可能
  76. 76. •  JavaScript  で設定した値を 直ぐに  ネイティブオブジェクトから利利⽤用可能 オブジェクトの相互運⽤用 JavaScript  での変更更がネイティブコードに反映 context.evaluateScript("obj.value = 'FromJS';") println(object.value)
  77. 77. •  ネイティブオブジェクトで設定した値を 直ぐに  JavaScript  から利利⽤用可能 オブジェクトの相互運⽤用 ネイティブコードでの変更更が  JavaScript  に反映 object.value = "FromNative" context.evaluateScript("var value=obj.value;") let value = context.objectForKeyedSubscript("value") println(value)
  78. 78. JavaScriptCore.framework 便便利利な使い⽅方 おまけ
  79. 79. 便便利利な使い⽅方 スクリプトを ファイルから読み込んで実⾏行行
  80. 80. var bundle = NSBundle.mainBundle() var path = bundle.pathForResource("Script",ofType:"js") let script = NSString(contentsOfFile:path, encoding:NSUTF8StringEncoding, error: nil) context.evaluateScript(script) let result = context.objectForKeyedSubscript("answer") スクリプトをファイルから読み込んで実⾏行行 バンドルからファイルを読み込む スクリプトをリソースとして管理理できる
  81. 81. function sum(array) { var result = 0; for (var i = 0; i < array.length; ++i) { result += array[i]; } return result; } var answer = sum([1,10,100,1000]); スクリプトをファイルから読み込んで実⾏行行 読み込む  JavaScript  ファイル 素のテキストとして扱えるので編集が簡単
  82. 82. 便便利利な使い⽅方 return  命令令で 終われるスクリプトにする
  83. 83. function sum(array) { var result = 0; for (var i = 0; i < array.length; ++i) { result += array[i]; } return result; } return sum([1,10,100,1000]); return  命令令で終われるスクリプトにする 読み込む  JavaScript  ファイル 最後を  return  命令令で終わらせたい
  84. 84. オブジェクトの相互運⽤用 SyntaxError: Return  statements  are only  valid  inside  functions そのまま使うと… return  命令令は関数内で使わなければいけない
  85. 85. return  命令令で終われるスクリプトにする スクリプトを実⾏行行時に匿匿名関数で包む let result = context.evaluateScript("(function(){(script)})();") •  スクリプトを関数内に⼊入れて関数を実⾏行行 •  return  の値は実⾏行行結果として取得可能 •  上記のとおり1⾏行行で記載すれば、 エラー時に通知される⾏行行番号が狂わない
  86. 86. JavaScriptCore.framework •  JavaScript  は⼿手軽に使えるスクリプト⾔言語 •  JavaScript  コードをアプリ内で簡単に実⾏行行 •  ネイティブオブジェクトとの相互運⽤用が可能 •  JavaScript  ライブラリをネイティブコードで活⽤用 •  コンパイル不不要でスクリプトを差し替え可能 •  カスタムスクリプト機能を実装するのに便便利利 可能性を秘めたフレームワーク

×