💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # [iOS Plugin Development Guide](http://cordova.apache.org/docs/en/latest/guide/platforms/ios/plugin.html) 本节详细介绍如何在iOS平台上实现本地插件代码。在阅读本文之前,请参阅[插件开发指南](插件开发指南.md),了解插件的结构及其常见的JavaScript接口。本节继续演示从Cordova webview到本机平台并返回的 *echo* 示例插件。 iOS插件实现为扩展 `CDVPlugin` 类的Objective-C类。对于要映射到Objective-C 类的JavaScript`exec` 方法的 `service` 参数,每个插件类必须在指定的应用程序目录的 `config.xml` 文件中注册为 `feature` 标记。 # 插件类映射 插件的JavaScript部分使用 `cordova.exec` 方法如下: ~~~ exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]); ~~~ 这会将来自 `UIWebView` 的请求整理到iOS本机端,有效地调用 `service` 类上的 `action` 方法,并在 `args` 数组中传递参数。 在Cordova-iOS应用程序项目的 `config.xml` 文件中将插件指定为 `feature` 标记,使用 `plugin.xml` 文件自动注入此标记,如[插件开发指南](插件开发指南.md)中所述: ~~~ <feature name="LocalStorage"> <param name="ios-package" value="CDVLocalStorage" /> </feature> ~~~ `feature` 的 `name` 属性应与您指定的JavaScript exec调用的`service` 参数相匹配。 `value`属性应该与插件的 Objective-C类的名称匹配。 `param` 的`name` 应始终为 `ios-package` 。如果您不遵循这些准则,插件可能会编译,但Cordova可能仍然无法访问它。 # 插件初始化和生命周期 在每个 `UIWebView`的生命周期中创建一个插件对象实例。除非首先通过JavaScript调用引用插件,否则不会实例化插件,除非在config.xml中将带有`onload name`属性的设置为“`true`”。例如, ~~~ <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](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVInvokedUrlCommand.h),[CDVPluginResult.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVPluginResult.h)和[CDVCommandDelegate.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/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*`,并将整个数组作为JavaScript回调的参数发送。这样,所有参数都会根据需要进行序列化或反序列化,因此将 `NSData*`作为 multipart 返回是安全的,而不是作为`Array`/`Dictionary`。 # Echo iOS插件示例 要匹配Application Plugins中描述的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` 数组的第一个参数 来取得 `echo`字符串,该参数对应于JavaScript `exec()` 函数传入的参数。 它检查参数以确保它不是`nil` 或空字符串,如果是,则返回具有 `ERROR` 状态的 `PluginResult` 。如果参数通过了检查,它将返回一个 `OK` 状态的 `PluginResult` ,并传入原始的 `echo`字符串。最后,它将结果发送到 `self.commandDelegate`,它在JavaScript端执行`exec`方法的成功或失败回调。如果调用成功回调,则传入`echo` 参数。 # iOS集成 `CDVPlugin` 类具有插件可以覆盖的其他方法。例如,您可以捕获 [pause](http://cordova.apache.org/docs/en/latest/cordova/events/events.html#pause) ,[resume](http://cordova.apache.org/docs/en/latest/cordova/events/events.html#resume),应用终止和`handleOpenURL`事件。有关指导,请参阅 [CDVPlugin.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVPlugin.h) 和 [CDVPlugin.m](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVPlugin.m) 类。 # 线程 插件方法通常在与主接口相同的线程中执行。如果你的插件需要大量的处理或者需要一个阻塞调用,你应该使用一个后台线程。例如: ``` - (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控制台中会记录一个错误。 * 不要忘记在白名单(whitelist)中添加您连接的任何主机,如[域白名单指南](http://cordova.apache.org/docs/en/latest/guide/appdev/whitelist/index.html)所述。如果您忘记了,Xcode控制台中会记录一个错误。