O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.
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...
Próximos SlideShares
Carregando em…5
×

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

28.179 visualizações

Publicada em

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

Publicada em: Software
  • Seja o primeiro a comentar

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  ライブラリをネイティブコードで活⽤用 •  コンパイル不不要でスクリプトを差し替え可能 •  カスタムスクリプト機能を実装するのに便便利利 可能性を秘めたフレームワーク

×