本文へ移動
rdlabo.devdocs

Cordovaプラグインへの組み込み

同じnpmパッケージ、Swiftライブラリ、Android AARがCapacitorとCordovaに対応します。Engineはプラグインのメンテナーが使うライブラリであり、個別に登録するCordovaプラグインではありません。Cordovaアプリに@capacitor/coreやCapacitorのネイティブSDKは不要です。

ネイティブビューと対応するDOMの配置、サイズ、重なり順は、利用側プラグインが引き続き管理します。native-adや#playerなどの既存セレクターを指定してください。DOMは初期化後に追加しても構いません。画像準備、スクロール処理、オーバーレイ、復元には共通のEngine実装を使います。

要件

標準WebViewを使用したcordova-ios 8.1.1とcordova-android 15.1.0で組み込みを検証しています。Engine自体にはiOS 15以上とAndroid API 23以上が必要ですが、CordovaホストやSDKがさらに高い最小バージョンを要求する場合があります。Androidの合成済みキャプチャにはAPI 26以上が必要です。

依存関係

Webパッケージとネイティブライブラリで、同じEngineリリースを使います。APIは1.0未満の段階なので、バージョンを固定し、依存関係のロックファイルを保持してください。

npm install --save-exact @webview-native-ui/engine@0.0.1

利用側プラグインでもネイティブ依存を宣言します。npmのインストールだけでは、いずれのネイティブライブラリもリンクされません。

Swift Package Managerの場合:

.package(url: "https://github.com/webview-native-ui/engine.git", exact: "0.0.1")
// プラグインターゲットの依存関係:
.product(name: "WebViewNativeUIEngine", package: "engine")

プラグインのAndroid Gradle設定の場合:

dependencies {
    implementation 'io.github.webview-native-ui:engine:0.0.1'
}

利用側ビルドの依存リポジトリにgoogle()とmavenCentral()を含めてください。

iOS

CordovaのSwiftPMプラグインでは、プラグインターゲットの依存としてCordovaとEngineの両方を宣言します。どちらのフレームワークもEngineターゲットの依存にはなりません。プラグインの既存SwiftPMプロダクトとソースファイルの宣言を維持します。

アプリからの最初の呼び出し前にレンダラーを表示する必要がある場合は、plugin.xmlのiOSプラットフォームセクションでプラグインを起動時に登録します。

<platform name="ios" package="swift">
    <config-file target="config.xml" parent="/*">
        <feature name="ExamplePlugin">
            <param name="ios-package" value="ExamplePlugin" />
            <param name="onload" value="true" />
        </feature>
    </config-file>
    <!-- プラグインの既存ソースファイル宣言を維持します。 -->
</platform>

プラットフォームセクションを重複追加せず、既存セクションへこれらの設定を統合してください。組み込み検証用の構成には、plugin.xml、SwiftPM、Gradleの完全な設定があります。

例では既存のレンダラーを拡張します。nativeViewは接続・配置済みのビューであり、Engineが作成するビューではありません。

import Cordova
import WebKit
import WebViewNativeUIEngine

@objc(ExamplePlugin)
class ExamplePlugin: CDVPlugin {
    private var projection: NativeUIProjection?
    private var nativeView: UIView?

    override func pluginInitialize() {
        // 既存のレンダラーでnativeViewをここで作成・接続・配置します。
        do {
            projection = try NativeUIProjection(
                webView: webView as? WKWebView,
                plugin: "ExamplePlugin",
                projectionSelector: "#player",
                receiveListeners: .init(
                    update: ["contentChanged"],
                    projection: .init(native: ["shown"], web: ["covered"], none: ["hidden"])
                ),
                runtime: .cordova,
                view: { [weak self] in self?.nativeView }
            )
        } catch {
            // 登録が利用できない場合は既存のWebフォールバックを維持します。
            NSLog("Native UI projection unavailable: %@", String(describing: error))
        }
    }

    override func dispose() {
        projection?.destroy()
        projection = nil
        super.dispose()
    }
}

iOSレンダラーを後から読み込む場合は、ビューを接続してから登録を作成します。レンダラーを置き換える前に、古い登録を破棄してください。

プラグインの既存の表示・コンテンツ通知を、projection?.receive("shown")、receive("hidden")、receive("contentChanged")で転送します。既存のコールバック・イベント送信箇所、またはプラグインの共通通知メソッドに追加してください。receiveはEngineセッションだけを更新し、アプリへ2回目のコールバックを送信しません。JS起動前に送信された、一致する投影先は初回接続用に保持されます。

Cordova iOS組み込みのWkWebView.convertFilePathが準備済みPNGを配信します。Engineが内部で解決するため、ファイルプラグイン、追加権限、開発者によるURL変換関数は不要です。既定の許可オリジンにはapp://localhostが含まれます。アプリのスキームやホスト名を変更した場合は、実際に信頼するオリジンを渡してください。

Android

既存のCordovaプラグインのGradle設定にMaven依存を宣言します。CordovaPluginPathHandlerのシグネチャを使う場合は、implementation 'androidx.webkit:webkit:1.14.0'も宣言してください。Cordova内部のWebKit依存は、プラグインのコンパイルクラスパスに公開されません。最初のWebViewナビゲーション前に初期化されるよう、プラグインのplugin.xmlのAndroidプラットフォームセクションに次の登録を統合します。

<platform name="android">
    <config-file target="res/xml/config.xml" parent="/*">
        <feature name="ExamplePlugin">
            <param name="android-package" value="com.example.ExamplePlugin" />
            <param name="onload" value="true" />
        </feature>
    </config-file>
    <!-- 既存のソースファイルとGradleフレームワークの宣言を維持します。 -->
</platform>
import android.view.View
import android.webkit.WebView
import io.github.webview_native_ui.engine.NativeUIProjection
import org.apache.cordova.CordovaPlugin
import org.apache.cordova.CordovaPluginPathHandler

class ExamplePlugin : CordovaPlugin() {
    private var projection: NativeUIProjection? = null
    private var nativeView: View? = null

    override fun pluginInitialize() {
        val web = webView.view as WebView
        projection = runCatching {
            // One origin-restricted channel per WebView, shared with later registrations.
            NativeUIProjection.install(web)
            // Existing renderer creates, attaches and positions nativeView here.
            NativeUIProjection(
                window = cordova.activity.window,
                webView = web,
                plugin = serviceName,
                projectionSelector = "#player",
                receiveListeners = NativeUIProjection.ReceiveListeners(
                    update = listOf("contentChanged"),
                    projection = NativeUIProjection.ReceiveListeners.Projection(
                        native = listOf("shown"), web = listOf("covered"), none = listOf("hidden")
                    )
                ),
                runtime = NativeUIProjection.Runtime.CORDOVA,
                view = { nativeView }
            )
        }.onFailure {
            android.util.Log.e(javaClass.simpleName, "Native UI projection unavailable", it)
        }.getOrNull()
    }

    override fun getPathHandler() = CordovaPluginPathHandler { path ->
        projection?.handleResource(path)
    }

    override fun onDestroy() {
        projection?.close()
        projection = null
        super.onDestroy()
    }
}

iOSと同様に、既存の通知箇所でprojection?.receive(eventName, data)を使います。ネイティブビューの作成と配置はUIスレッドで行います。レンダラーを非同期に読み込む場合は、install(web)をpluginInitialize()に残し、ビューの準備後に登録を作成してください。そのレンダラーを置き換える前に、古い登録を閉じます。

PathHandlerは同一オリジンの画像リクエストをEngineへ委譲します。アプリ専用キャッシュストレージにある、その登録のPNGだけを配信し、無関係なパスにはnullを返します。プラグインに既存ハンドラーがある場合は、projection?.handleResource(path) ?: existingHandler.handle(path)で組み合わせます。登録が複数ある場合は、一致する所有者へ委譲します。ハンドラーはWebViewの生存期間中、インストールされたままにしてください。WebViewClientの置き換えやストレージ権限は不要です。

既定値はCordovaのhttps://localhostとhttp://localhostに対応します。ホスト名を変更する場合は、installと各登録に同じ明示的なオリジン集合を渡してください。この組み込みでは、ワイルドカードオリジンとfile://のアプリオリジンには対応しません。

生存期間と検証

WebViewの生存期間中は登録を保持します。Engineはドキュメント再読み込み後に再接続し、古いドキュメントの画像状態を消去します。プラグインがonResetでネイティブUIを破棄する場合は、ビュープロバイダーを更新して適切な投影先を通知してください。破棄済みビューを指す登録を残してはいけません。プラグイン・WebViewを破棄するときに登録を解放します。

Capacitorの登録は既存の既定値を維持します。Cordovaの組み込みには標準のCordova WebViewリソースハンドラーが必要です。Capacitorアプリ内でCordovaプラグインを動かす構成はこの組み込みの対象外で、ホストはCapacitor向けの組み込みを使う必要があります。セレクター、レイアウト、キャプチャの規則は共通のDOMとネイティブサーフェスの要件を参照してください。Ionicオーバーレイ対応は任意で、通常のCordovaアプリでは有効化不要です。

組み込み検証用の構成は、Cordova CLI 13、cordova-ios 8.1.1、cordova-android 15.1.0と標準WebViewを使います。JSブリッジのモックだけでなく、実際のPNG受け渡し、ネイティブ復元、ドキュメント再読み込み後の非表示状態の保持を検証します。古いCordovaリリースやカスタムWebViewエンジンは、別途検証が必要です。

起動済みのシミュレーター・エミュレーターで、実際の利用側のチェックを実行します。

npm run test:cordova -- ios SIMULATOR_UDID
npm run test:cordova -- android emulator-5554

スクリプトは使い捨てのアプリを作成し、ローカルのSwiftPM・MavenのEngineアーティファクトを使って、表示された一時ディレクトリにアプリの実行結果を記録します。npmやMavenへの公開は行いません。AndroidにはJDK 21以上、SDK 36、ANDROID_HOMEが、iOSにはXcodeが必要です。