ScriptMediator: registerFunction()

ネイティブ側で定義した関数を指定した関数名で登録し、JavaScript側から呼び出せるようにします。

構文

suspend ScriptMediator.registerFunction(isSync, functionName, funciton): Any?
ScriptMediator.registerFunction(isSync, functionName, function, completionHandler): Unit
ScriptMediator.registerFunction(isSync, functionName, function) async -> Any?
ScriptMediator.registerFunction(isSync, functionName, function, completionHandler) -> Void

引数

  • isSync: Boolean

    JavaScript側で同期関数として登録するかどうかです。

    引数isSync の値が true なら、引数 function として与えた関数をJavaScript側から呼び出した際、JavaScript上では関数呼び出しは同期関数として行われます。つまりJavaScript側では、登録されたネイティブ関数の実行が完了し値を返すまで、次の処理へ進みません。

    引数 isSync の値が false ならJavaScript上では関数呼び出しは非同期関数として行われます。つまりJavaScript側では、登録されたネイティブ関数の実行が完了する際に履行される Promise が即時に返されます。

  • functionName: String

    登録する関数の名前です。

    この名前はJavaScript側で登録されたネイティブ関数を呼び出す際に使います。 JavaScript側では、引数 functionName として指定した名前がそのまま関数名として使われます。

    JavaScriptの識別子
    • JavaScriptにおいて、識別子として使える文字種別と文字の出現位置には制限があります。 使用の制限された文字を含むプロパティへアクセスするには、object["プロパティ名"] のように、プロパティ名を文字列として指定する必要があります。 詳細は MDN, JavaScriptリファレンス, 字句文法を参照してください。
  • function: (Array<out Any?>) -> Any?

    登録する関数です。

    引数として任意の型の配列を1つ受けとり、任意の型の値を返すラムダ式を指定します。

    登録された関数は、JavaScript側から Alier.Native.<関数名> に対して関数呼び出しを行うことで、実行できるようになります(<関数名> には引数 functionName で指定した名前を使います)。

  • completionHandler: ((Any?) -> Unit)? (省略可)

    関数登録の完了後に呼び出されるコールバック関数です。 null を指定した場合、何も行いません。

    この引数の指定は任意です。指定がない場合、この関数は suspend 関数として実行されます。

    suspend 関数の実行
    • suspend 関数の呼び出しは suspend 関数の定義部にのみ書けます。 通常の(suspend でない)関数から suspend 関数を実行するには、コルーチンビルダー(suspend 関数を受け取り Job を生成する関数)を使います。 詳細は Kotlin Documentation, Coroutines basics を参照してください。
  • isSync: Bool?

    JavaScript側で同期関数として登録するかどうかです。

    引数isSync の値が true なら、引数 function として与えた関数をJavaScript側から呼び出した際、JavaScript上では関数呼び出しは同期関数として行われます。つまりJavaScript側では、登録されたネイティブ関数の実行が完了し値を返すまで、次の処理へ進みません。

    引数 isSync の値が false ならJavaScript上では関数呼び出しは非同期関数として行われます。つまりJavaScript側では、登録されたネイティブ関数の実行が完了する際に履行される Promise が即時に返されます。

  • functionName: String

    登録する関数の名前です。

    この名前はJavaScript側で登録されたネイティブ関数を呼び出す際に使います。 JavaScript側では、引数 functionName として指定した名前がそのまま関数名として使われます。

    JavaScriptの識別子
    • JavaScriptにおいて、識別子として使える文字種別と文字の出現位置には制限があります。 使用の制限された文字を含むプロパティへアクセスするには、object["プロパティ名"] のように、プロパティ名を文字列として指定する必要があります。 詳細は MDN, JavaScriptリファレンス, 字句文法を参照してください。
  • function: ([Any?]) -> Any? | ([Any?]) throws -> Any?

    登録する関数です。

    引数として任意の型の配列を1つ受けとり、任意の型の値を返すクロージャを指定します。

    登録された関数は、JavaScript側から Alier.Native.<関数名> に対して関数呼び出しを行うことで、実行できるようになります(<関数名> には引数 functionName で指定した名前を使います)。

  • completionHandler: ((Any?) -> Void)? (省略可)

    関数登録の完了後に呼び出されるコールバック関数です。 nil を指定した場合、何も行いません。

    この引数の指定は任意です。指定がない場合、この関数は async 関数として実行されます。 async 関数は iOS 15 以上でのみサポートされています

    async 関数の実行
    • async 関数の呼び出しは、async 関数の定義部かアプリケーションのエントリポイントとなる(つまりトップレベルの)関数の定義部にのみ書けます(エントリポイントは @main 属性の与えられたデータ型が持つ static main() 関数として定義します)。 通常の(async でない)関数から async 関数を実行するには、Task を使います。 詳細は The Swift Programming Language, Concurrency, The Swift Programming Language, Attributes を参照してください。
実引数の型変換
  • JavaScriptの型システムにおける固定長の数値型は number 型のみです。 number 型の値はIEEE754 binary64 形式の 64 ビット2進浮動小数点数として表されます。 これは Kotlin や Swift における Double 型と同じです。

    従ってAlierフレームワークでは、JavaScript側から与えられた number 型の値は、ネイティブ側では Double として扱われます。 整数を期待するネイティブ関数においても、直接 Int のような整数型へキャストを行うのではなく、Double としてキャストした上で適切な変換関数を呼び出す必要があります

返値: Any?

関数登録の結果です。

返値は suspend 関数として呼び出した場合にのみに返ります引数 completionHandler を指定した場合、この関数は返値を持ちません

関数登録の結果です。

返値は async 関数として呼び出した場合にのみに返ります引数 completionHandler を指定した場合、この関数は返値を持ちません

例外

  • IllegalArgumentException
    • 引数 functionName で指定した名前が既に登録されている場合。
  • InvocationError.FUNCTION_NAME_DUPLICATED
    • 引数 functionName で指定した名前が既に登録されている場合。

以下では registerFunction() メソッドを使って、Kotlin側で定義した関数(ラムダ式)をJavaScript側から呼び出せるようにする例を示します。

val add: (Array<out Any?>) -> Any? = { args ->
    if (args.size < 2) { return 0 }
    val x: Number = args[0] as? Number ?: return 0
    val y: Number = args[1] as? Number ?: return x.toInt()

    x.toInt() + y.toInt()
}
val collatz: ((Array<out Any?>) -> Any?) = { args ->
    val seq = arrayListOf<Int>()

    if (args.size >= 1) {
        val n0 = (args[0] as? Number)?.toInt()
        if (n0 != null && n0 != 0) {
            var n = if (n0 < 0) { -n0 } else { n0 }

            while (n >= 1) {
                n = n / (n.and(-n))
                if (n == 1) { break }
                seq.add(n)
                n = 3 * n + 1
            }
        }
    }
    seq.toTypedArray()
}

try {
    //  Alier.Native.add() をJavaScript側に定義します。
    //  registerFunction は既に "add" に対して関数が登録されている場合に
    //  IllegalArgumentException 例外を送ります。
    scriptMediator.registerFunction(
        isSync        = false,
        functionName  = "add",
        function      = add
    )
    //  isSync を true にすると、JavaScript 側では Promise を返す関数
    //  として定義されます。
    //  Promise の履行を待ち合わせることで関数の実行結果が得られます。
    scriptMediator.registerFunction(
        isSync        = true,
        functionName  = "collatz",
        function      = collatz
    )
} catch (error: IllegalArgumentException) {
    //  ここに例外からの復旧処理を書きます。
    //  復旧処理の後、後続処理を継続できるならブロックを抜け、
    //  そうでないなら例外を再度 throw します。
}

以下では registerFunction() メソッドを使って、Swift側で定義した関数(クロージャ)をJavaScript側から呼び出せるようにする例を示します。

//  登録する関数を定義します。
let add: ([Any?]) -> Any? = { args in
    if args.count < 2 { return 0 }
    guard let x = args[0] as? Double else { return 0 }
    guard let y = args[1] as? Double else { return Int(x) }

    return Int(x) + Int(y)
}
let collatz: ([Any?]) -> Any? = { args in
    var seq: [Int] = []

    if args.count < 1 { return seq }
    guard let n0 = args[0] as? Double else { return seq }

    var n = Int(n0)

    if n == 0 { return seq }
    if n < 0 { n = -n }

    while n >= 1 {
        n /= (n & (-n))
        if n == 1 { break }
        seq.append(n)
        n = 3 * n + 1
    }

    return seq
}


do {
    //  Alier.Native.add() をJavaScript側に定義します。
    //  registerFunction は既に "add" に対して関数が登録されている場合に
    //  例外を送ります。
    try scriptMediator.registerFunction(
        isSync      : false,
        functionName: "add",
        function    : add
    )
    //  isSync を true にすると、JavaScript 側では Promise を返す関数
    //  として定義されます。
    //  Promise の履行を待ち合わせることで関数の実行結果が得られます。
    try scriptMediator.registerFunction(
        isSync      : true,
        functionName: "collatz",
        function    : collatz
    )
} catch {
    //  ここに例外からの復旧処理を書きます。
    //  復旧処理の後、後続処理を継続できるならブロックを抜け、
    //  そうでないなら例外を再度 throw します。
}

JavaScript 側でネイティブアプリ側から登録した関数を呼び出します。

//  add() は同期関数として登録されているため、計算結果がそのまま返されます。
//  定義側では単一の配列を受け取りますが、
//  呼び出し側では単に必要な個数の引数を指定します。
console.log(Alier.Native.add(1, 2));

//  collatz() は非同期関数として登録されているため、Promise が返されます。
//  計算結果を得るには await 演算子を使います。
console.log(await Alier.Native.collatz(42));

解説

ネイティブ側で定義した関数を指定した関数名で登録し、JavaScript側から呼び出せるようにします。

登録された関数はJavaScript側で Alier.Native.<関数名>() を呼び出すことで実行されます。<関数名> の部分は引数 functionName で与えた名前が使われます。 また、登録側の関数は単一の配列を引数を受け取りますが、呼び出し側では単に必要な個数の引数を指定します(配列への変換はフレームワーク側で行われます)。

例えば引数 functionName として "add" を指定したなら、JavaScript側には Alier.Native に対して add という名前のプロパティが追加されます。 また登録された関数 add が引数配列の最初の 2 つを使うなら、呼び出し側では Alier.Native.add(1, 2) のように 2 つの引数を指定します。

登録しようとした名前が既に使われている場合、IllegalArgumentException が発生します。 重複の検査が不要で、常に関数の登録を成功させたい場合、代わりに replaceFunction() を使用してください。

ネイティブ側で定義した関数を指定した関数名で登録し、JavaScript側から呼び出せるようにします。

登録された関数はJavaScript側で Alier.Native.<関数名>() を呼び出すことで実行されます。<関数名> の部分は引数 functionName で与えた名前が使われます。 また、登録側の関数は単一の配列を引数を受け取りますが、呼び出し側では単に必要な個数の引数を指定します(配列への変換はフレームワーク側で行われます)。

例えば引数 functionName として "add" を指定したなら、JavaScript側には Alier.Native に対して add という名前のプロパティが追加されます。 また登録された関数 add が引数配列の最初の 2 つを使うなら、呼び出し側では Alier.Native.add(1, 2) のように 2 つの引数を指定します。

登録しようとした名前が既に使われている場合、InvocationError.FUNCTION_NAME_DUPLICATED が発生します。 重複の検査が不要で、常に関数の登録を成功させたい場合、代わりに replaceFunction() を使用してください。