この記事では、JavaScriptを使用してネイティブAPIにアクセスする方法の基本概念を説明します。 私たちの焦点は、JavaScriptと対応するネイティブプラットフォーム間でプリミティブ型がどのようにマッピングされるかです。 次に、複雑なオブジェクトがどのように表されアクセスされるかを説明します。 最後に、AndroidおよびiOS開発プラットフォーム用のTypeScript定義を提供するTypeScriptとtns-platform-declarationsアドオンについて説明します。
NativeScriptを使用すると、基盤となるプラットフォームからすべてのネイティブAPIにアクセスできます。 この動作を実現するために、多くのことが内部で発生します。それらの1つはマーシャリング(JavaScriptと、iOSのObjective-Cデータ型とAndroidのJavaデータ型間の変換)です。
この記事では、さまざまなデータ型パラメーターを使用してJavaScriptからネイティブAPIを呼び出す方法を学習します。 詳細については、iOSランタイムおよび Androidランタイムセクションのデータ変換に関するプラットフォーム固有のリソースを参照してください。
すべてのネイティブ数値型(例:iOSではchar、short、int、double、float、Androidではbyte、short、int、long、double、float)は、暗黙的にJavaScript数値に変換されます。 たとえば、iOSで次のコードを実行すると:
console.log(`pow(2.5, 3) = ${pow(2.5, 3)}`);
console.log(`pow(2.5, 3) = ${pow(2.5, 3)}`);
iOSランタイムはJavaScriptの数値引数をネイティブのdoubleに変換し、ネイティブのpow(double x, double y)関数に渡します。 返されたネイティブ整数は自動的にJavaScriptの数値に変換され、console.log()に渡されます。同じことがAndroidにも当てはまります。
console.log(`min(3, 4) = ${java.lang.Math.min(3, 4)}`);
console.log(`min(3, 4) = ${java.lang.Math.min(3, 4)}`);
ネイティブのjava.lang.Math.min()メソッドでは2つの整数が必要です。 Androidランタイムは、関数java.lang.Math.min()の構文を認識し、引数3と4をJava整数データ型の表現に変換します。 返された整数もJavaScriptの数値に自動的に変換され、console.log()に渡されます。
JavaScript文字列は暗黙的にAndroidではjava.lang.Stringに、iOSではNSStringにマーシャリングされ、その逆も同様です。
let button = new UIButton();
button.setTitleForState('Button title', UIControlStateNormal); // 'Button title' is converted to NSString
console.log(button.titleLabel.text); // The returned NSString is converted to JavaScript string
let button = new UIButton();
button.setTitleForState('Button title', UIControlStateNormal); // 'Button title' is converted to NSString
console.log(button.titleLabel.text); // The returned NSString is converted to JavaScript string
const file = new java.io.File('myfile.txt'); // 'myfile.txt' is converted to java.lang.String
const file = new java.io.File('myfile.txt'); // 'myfile.txt' is converted to java.lang.String
これの例外は、instancetype(initメソッドとファクトリーメソッド)を返すように宣言されたNSStringクラスのメソッドです。 つまり、Objective-Cの戻り値の型がinstancetypeであるNSString.stringWithStringの呼び出しは、 JavaScript文字列ではなくNSStringインスタンスのラッパーを返します。
JavaScriptの論理値は、Androidではboolean、とiOSではBOOLに暗黙的にマーシャリングされ、その逆も同様です。
let str = NSString.stringWithString('YES');
let isTrue = str.boolValue();
let str = NSString.stringWithString('YES');
let isTrue = str.boolValue();
let str = new java.lang.String('Hello world!');
let result = str.endsWith('world!');
console.log(result); // true
let str = new java.lang.String('Hello world!');
let result = str.endsWith('world!');
console.log(result); // true
JavaScript配列は、AndroidおよびiOS上ではNSArrayの、特殊なJava配列にマップされます。
// nsArray is not a JavaScript array but a JavaScript wrapper around a native NSArray
let nsArray = NSArray.arrayWithArray(['Four', 'Five', 'Two', 'Seven']);
let jsArray = ['One', 'Two', 'Three']; // pure JavaScript array
let firstCommon = nsArray.firstObjectCommonWithArray(jsArray);
console.log(firstCommon); // Two
// nsArray is not a JavaScript array but a JavaScript wrapper around a native NSArray
let nsArray = NSArray.arrayWithArray(['Four', 'Five', 'Two', 'Seven']);
let jsArray = ['One', 'Two', 'Three']; // pure JavaScript array
let firstCommon = nsArray.firstObjectCommonWithArray(jsArray);
console.log(firstCommon); // Two
次のコードスニペットは、JavaScriptからns.example.Math.minElement(int[] array)を呼び出す方法を示しています。
let numbers = [3, 6, 19, -2, 7, 6];
let min = ns.example.Math.minElement(numbers); // -2
let numbers = [3, 6, 19, -2, 7, 6];
let min = ns.example.Math.minElement(numbers); // -2
JavaScriptの世界では、すべてのネイティブクラスはコンストラクター関数によって表されます。 ネイティブクラスの各静的メソッドはJavaScriptコンストラクター関数の関数になり、各インスタンスメソッドはJavaScriptプロトタイプの関数になります。 非常に直感的ですが、JavaScriptを介してオブジェクトをインスタンス化し、メソッドを呼び出すには、いくつかの特徴があります(特にiOSの場合)。
JavaScriptでNSMutableArrayクラスのインスタンスが作成および利用される方法の例を次に示します。
let array = new NSMutableArray();
array.addObject(new NSObject());
let array = new NSMutableArray();
array.addObject(new NSObject());
このスニペットは、NSMutableArrayのインスタンスを作成し、addObject(object)メソッドを使用してオブジェクトを追加します。 カーテンの後ろで起こることは次のとおりです。new NSMutableArray()は、iOSランタイムによって[[NSMutableArray alloc] init]の呼び出しに変換されます。 このインスタンスは、JavaScriptオブジェクトにラップされ、配列変数に格納されます。 プロトタイプチェーンには、NSMutableArray(およびその基底クラス)によって公開されているすべてのパブリックプロパティとメソッドが含まれています。 addObject(object)の呼び出しは簡単ですが、Objective-CセレクターをJavaScript関数にマッピングする方法を定義するいくつかの単純なルールに従って、より多くの引数を使用してObjective-Cメソッドを呼び出します。 次のNSMutableArrayセレクター、replaceObjectsInRange:withObjectsFromArray:range:を考えてみましょう。 JavaScriptでは、次の関数で表されます:replaceObjectsInRangeWithObjectsFromArrayRange(objectsToRange、souceArray、sourceRange)(引数名は任意です)。 ここで、関数名は、Objective-Cセレクターで定義された引数の名前を最初の引数に小さい文字で開始し、その後に大文字を追加することで生成されることに注意してください。
ほとんどの場合、NSDictionaryインスタンスをパラメーターとして受け入れるメソッドに遭遇します。NSDictionaryインスタンスを作成する方法はいくつかあります。
NSDictionary
キーと値の配列の使用と受け渡し
let dict = new NSDictionary([".example.com", "cookieName", "/", "cookieValue"], [NSHTTPCookieDomain, NSHTTPCookieName, NSHTTPCookiePath,NSHTTPCookieValue]);
let cookie = NSHTTPCookie.cookieWithProperties(dict);
let dict = new NSDictionary([".example.com", "cookieName", "/", "cookieValue"], [NSHTTPCookieDomain, NSHTTPCookieName, NSHTTPCookiePath,NSHTTPCookieValue]);
let cookie = NSHTTPCookie.cookieWithProperties(dict);
let cookie = NSHTTPCookie.cookieWithProperties({[NSHTTPCookieDomain]:".example.com", [NSHTTPCookieName]:"cookieName", [NSHTTPCookiePath]:"/", [NSHTTPCookieValue]:"cookieValue"});
let cookie = NSHTTPCookie.cookieWithProperties({[NSHTTPCookieDomain]:".example.com", [NSHTTPCookieName]:"cookieName", [NSHTTPCookiePath]:"/", [NSHTTPCookieValue]:"cookieValue"});
2番目の例では、JSONリテラルをメソッドに渡します。 NSHTTPCookieDomainは変数であり、その値を得るために計算されたプロパティ名を使用する必要があります(そうでなければ、キーとして"NSHTTPCookieDomain"を取得しています)。
次のコードスニペットは、JavaScriptでandroid.widget.Buttonのインスタンスが作成される方法を示しています。
let context = ...;
let button = new android.widget.Button(context);
button.setText("My Button"); // "My Button" is converted to java.lang.String
let context = ...;
let button = new android.widget.Button(context);
button.setText("My Button"); // "My Button" is converted to java.lang.String
上記のとおり、ネイティブJava型は、対応するパッケージを通じて公開されています。 つまり、ネイティブJava型にアクセスするには、含まれているパッケージを知り、明示的に指定するだけです。 ネイティブJavaメソッドは、通常のJavaScriptメソッドと同じ方法(メソッド識別子を使用し、必要な引数を指定する)でアクセスされます。 AndroidでのJavaパッケージの詳細については、こちらをご覧ください。
JavaScriptのUndefinedおよびNullは、 JavaのnullポインターとObjective-Cのnilに変換されます。ネイティブのnull値はJavaScriptのnullに変換されます。
console.log(NSStringFromClass(null)); // null
console.log(NSStringFromClass(null)); // null
let context = ...;
const button = new android.widget.Button(context);
button.setOnClickListener(undefined); // the Java call will be made using the null keyword
let context = ...;
const button = new android.widget.Button(context);
button.setOnClickListener(undefined); // the Java call will be made using the null keyword
ネイティブAPIにアクセスしてIntellisenseを使用するには、開発者の依存関係をtns-platform-declarationsに追加する必要があります。
インストールして有効にする手順
npm install tns-platform-declarations --save-dev
npm i tns-platform-declarations --save-dev
フラグ)としてインストールし、
出力されるビルドファイルに非常に大きな宣言ファイルが含まれないようにします。
ルートプロジェクトディレクトリにreference.d.tsを作成し、次を追加します。
デフォルトでは、android.d.tsファイルにはAPIレベル17用に生成されたタイピングが付属しています。
Android開発者は、新しいAPIレベルで導入された特定のクラス、メソッド、またはプロパティにアクセスする必要があります。
tns-platform-declarationsプラグインは、各サポートライブラリから関連タイピングを含む17から27までのすべてのAPIレベルのために生成されたタイピングが付属しています。
特定のAndroidレベルでタイピングを使用するには、デフォルトの宣言ファイルへの参照を優先するものに置き換えます。
各APIレベルのファイルには、ダッシュの後にAPIレベルの番号が続きます(たとえば、API 21の場合、ファイルの名前はandroid-21.d.ts
)。
たとえば、API 21+用のアプリケーションを開発しており、そのAPIレベルに対して生成された入力が必要であると仮定します。
tsconfig.json
を以下の設定を含むように変更します。
d.tsファイルには大量のメモリとCPUが必要であることに注意してください。 tsconfig.jsonにskipLibCheckオプションをに追加することを検討してください。 詳細については、tns-platform-declarationsのGitHubリポジトリを参照してください。