iOS 插件开发指南

本节详细介绍如何在 iOS 平台上实现原生插件代码。

在阅读本节之前,请参阅 插件开发指南,了解插件的结构及其常见的 JavaScript 接口概述。本节将继续演示示例 echo 插件,该插件在 Cordova webview 与原生平台之间进行通信。

iOS 插件实现为扩展 CDVPlugin 类的 Objective-C 类。为了使 JavaScript 的 exec 方法的 service 参数映射到 Objective-C 类,每个插件类都必须在命名应用程序目录的 config.xml 文件中注册为 <feature> 标签。

插件类映射

插件的 JavaScript 部分使用 cordova.exec 方法,如下所示

exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);

这将从 UIWebView 传递一个请求到 iOS 原生端,有效地调用 service 类的 action 方法,并使用 args 数组中传递的参数。

在您的 Cordova-iOS 应用程序项目的 config.xml 文件中将插件指定为 <feature> 标签,使用 plugin.xml 文件自动注入此标记,如 插件开发指南 中所述。

<feature name="LocalStorage">
    <param name="ios-package" value="CDVLocalStorage" />
</feature>

功能的 name 属性应与您在 JavaScript exec 调用的 service 参数中指定的属性匹配。value 属性应与插件的 Objective-C 类的名称匹配。<param> 元素的 name 应始终为 ios-package。如果您不遵循这些准则,插件可能会编译,但 Cordova 仍然可能无法访问它。

插件初始化和生命周期

每个 UIWebView 的生命周期都会创建一个插件对象的实例。除非在 config.xml 中将 <param>onload name 属性设置为 "true",否则插件不会在第一次被 JavaScript 调用之前实例化。例如,

<feature name="Echo">
    <param name="ios-package" value="Echo" />
    <param name="onload" value="true" />
</feature>

插件应使用 pluginInitialize 方法进行启动逻辑。

具有长时间运行的请求或后台活动(如媒体播放、监听器或维护内部状态)的插件应实现 onReset 方法以取消这些长时间运行的请求或清理这些活动。当 UIWebView 导航到新页面或刷新时,该方法会运行,这会重新加载 JavaScript。

编写 iOS Cordova 插件

JavaScript 调用会向原生端发出插件请求,相应的 iOS Objective-C 插件在 config.xml 文件中正确映射,但最终的 iOS Objective-C 插件类是什么样的?通过 JavaScript 的 exec 函数分派到插件的任何内容都会传递到相应的插件类的 action 方法中。插件方法具有以下签名

- (void)myMethod:(CDVInvokedUrlCommand*)command
{
    CDVPluginResult* pluginResult = nil;
    NSString* myarg = [command.arguments objectAtIndex:0];

    if (myarg != nil) {
        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
    } else {
        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was null"];
    }
    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

有关更多详细信息,请参阅 CDVInvokedUrlCommand.hCDVPluginResult.hCDVCommandDelegate.h

iOS CDVPluginResult 消息类型

您可以使用 CDVPluginResult 将各种结果类型返回到 JavaScript 回调,使用遵循以下模式的类方法

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAs...

您可以创建 StringIntDoubleBoolArrayDictionaryArrayBufferMultipart 类型。您也可以省略任何参数以发送状态,或返回错误,甚至可以选择不发送任何插件结果,在这种情况下,两个回调都不会触发。

请注意以下有关复杂返回值的信息

  • messageAsArrayBuffer 预期 NSData* 并将其转换为 JavaScript 回调中的 ArrayBuffer。同样,JavaScript 发送到插件的任何 ArrayBuffer 都将转换为 NSData*

  • messageAsMultipart 预期 NSArray* 包含任何其他支持的类型,并将整个数组作为 arguments 发送到您的 JavaScript 回调。这样,所有参数都将根据需要进行序列化或反序列化,因此将 NSData* 作为多部分返回是安全的,但不能作为 Array/Dictionary 返回。

Echo iOS 插件示例

为了匹配应用程序插件中描述的 JavaScript 接口的 echo 功能,请使用 plugin.xmlfeature 规范注入到本地平台的 config.xml 文件中

<platform name="ios">
    <config-file target="config.xml" parent="/*">
        <feature name="Echo">
            <param name="ios-package" value="Echo" />
        </feature>
    </config-file>
</platform>

然后,我们将以下 Echo.hEcho.m 文件添加到 Cordova-iOS 应用程序目录中的 Plugins 文件夹中

/********* Echo.h Cordova Plugin Header *******/

#import <Cordova/CDVPlugin.h>

@interface Echo : CDVPlugin

- (void)echo:(CDVInvokedUrlCommand*)command;

@end

/********* Echo.m Cordova Plugin Implementation *******/

#import "Echo.h"
#import <Cordova/CDVPlugin.h>

@implementation Echo

- (void)echo:(CDVInvokedUrlCommand*)command
{
    CDVPluginResult* pluginResult = nil;
    NSString* echo = [command.arguments objectAtIndex:0];

    if (echo != nil && [echo length] > 0) {
        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
    } else {
        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
    }

    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

@end

文件顶部的必要导入从 CDVPlugin 扩展类。在本例中,插件只支持一个 echo 操作。它通过调用 objectAtIndex 方法获取 arguments 数组的第一个参数来获取回声字符串,该参数对应于 JavaScript exec() 函数传递的参数。

它检查参数以确保它不是 nil 或空字符串,如果这样,则返回具有 ERROR 状态的 PluginResult。如果参数通过检查,它将返回具有 OK 状态的 PluginResult,并将原始 echo 字符串传递进去。最后,它将结果发送到 self.commandDelegate,后者在 JavaScript 端执行 exec 方法的成功或失败回调。如果成功回调被调用,它将传递 echo 参数。

iOS 集成

CDVPlugin 类具有其他方法,您的插件可以覆盖这些方法。例如,您可以捕获 暂停恢复、应用程序终止和 handleOpenURL 事件。请参阅 CDVPlugin.hCDVPlugin.m 类以获取指导。

WKURLSchemeTask 钩子

WKURLSchemeTask 是 Cordova 的主要 WKWebView 用于从您的应用程序包加载文件的接口。您可以通过在插件中实现 - (BOOL) overrideSchemeTask: (id <WKURLSchemeTask>)urlSchemeTask 方法来创建自己的自定义方案或自定义加载代码,用于 webview。

线程

插件方法通常在与主界面相同的线程中执行。如果您的插件需要大量处理或需要阻塞调用,您应该使用后台线程。例如

- (void)myPluginMethod:(CDVInvokedUrlCommand*)command
{
    // Check command.arguments here.
    [self.commandDelegate runInBackground:^{
        NSString* payload = nil;
        // Some blocking logic...
        CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
        // The sendPluginResult method is thread-safe.
        [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
    }];
}

调试 iOS 插件

要在 Objective-C 端进行调试,您需要 Xcode 的内置调试器。对于 JavaScript,您可以将 Safari 附加到在 iOS 模拟器/设备中运行的应用程序。

常见陷阱

  • 不要忘记将插件的映射添加到 config.xml。如果您忘记,Xcode 控制台中会记录错误。

  • 不要忘记在允许列表中添加您连接的任何主机,如域 允许列表指南 中所述。如果您忘记,Xcode 控制台中会记录错误。