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.h、CDVPluginResult.h 和 CDVCommandDelegate.h。
iOS CDVPluginResult 消息类型
您可以使用 CDVPluginResult
将各种结果类型返回到 JavaScript 回调,使用遵循以下模式的类方法
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAs...
您可以创建 String
、Int
、Double
、Bool
、Array
、Dictionary
、ArrayBuffer
和 Multipart
类型。您也可以省略任何参数以发送状态,或返回错误,甚至可以选择不发送任何插件结果,在这种情况下,两个回调都不会触发。
请注意以下有关复杂返回值的信息
-
messageAsArrayBuffer
预期NSData*
并将其转换为 JavaScript 回调中的ArrayBuffer
。同样,JavaScript 发送到插件的任何ArrayBuffer
都将转换为NSData*
。 -
messageAsMultipart
预期NSArray*
包含任何其他支持的类型,并将整个数组作为arguments
发送到您的 JavaScript 回调。这样,所有参数都将根据需要进行序列化或反序列化,因此将NSData*
作为多部分返回是安全的,但不能作为Array
/Dictionary
返回。
Echo iOS 插件示例
为了匹配应用程序插件中描述的 JavaScript 接口的 echo 功能,请使用 plugin.xml
将 feature
规范注入到本地平台的 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.h
和 Echo.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.h 和 CDVPlugin.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 控制台中会记录错误。