blob: be6e916faadaf0638f4f4b0109b2a76dc2ea3772 [file] [log] [blame] [view]
---
license: Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
---
# Developing a Plugin on iOS
A plugin is an Objective-C class that extends the `CDVPlugin` class.
Each plugin class must be registered using the config.xml file, as a <plugin> tag under the <plugins> key.
## Plugin Class Mapping
The JavaScript portion of a plugin always uses the `cordova.exec` method as follows:
exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);
This marshals a request from the `UIWebView` to the iOS native side,
more or less boiling down to calling the `action` method on the
`service` class, with the arguments passed in the `args` array.
The plugin must be added under the `<plugins>` tag of your Cordova-iOS
application's project's `config.xml` file.
<plugin name="service_name" value="PluginClassName" />
The key `service_name` should match what you use in the JavaScript
`exec` call, and the value matches the name of the plugin's
Objective-C class. Otherwise the plugin may compile but would not be
reachable by Cordova.
## Plugin Initialization and Lifetime
There is one instance of a plugin object that is created per-UIWebView, and the lifetime of the instance is tied to the UIWebView. Plugins are not instantiated until they are first referenced by a call from JS, unless the `onload` attribute set within config.xml. E.g.:
<plugin name="Echo" value="Echo" onload="true" />
There is *no* designated initializer for plugins. Instead, plugins should use the `pluginInitialize` method for their start-up logic.
Plugins with long-running requests, background activity (e.g. playing media), listeners or internal state should implement the `onReset` method and stop or clean up those activities. This method is run when the `UIWebView` navigates to a new page or refreshes, which reloads the JavaScript.
## Writing an iOS Cordova Plugin
We have JavaScript fire off a plugin request to the native side. We have the iOS Objective-C plugin mapped properly via the `config.xml` file. So what does the final iOS Objective-C Plugin class look like?
What gets dispatched to the plugin via JavaScript's `exec` function gets passed into the corresponding Plugin class's `action` method. A plugin method has this signature:
- (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];
}
1. [CDVInvokedUrlCommand.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/CDVInvokedUrlCommand.h)
2. [CDVPluginResult.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/CDVPluginResult.h)
3. [CDVCommandDelegate.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/CDVCommandDelegate.h)
## iOS CDVPluginResult message types
Using CDVPluginResult you can return a variety of result types back to your JavaScript callbacks, using class methods that look like:
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAs...
You can create `String`, `Int`, `Double`, `Bool`, `Array`,
`Dictionary`, `ArrayBuffer`, and `Multipart` types. Or, don't attach
any arguments (just send a status). Or, return an Error. You can
even choose to not send any plugin result at all, in which case the
callback does not fire.
### Notes
* `messageAsArrayBuffer` expects `NSData*` and converts to an `ArrayBuffer` for your JavaScript callback (and `ArrayBuffers` sent to a plugin from JavaScript are converted to `NSData*`).
* `messageAsMultipart` expects an `NSArray*` containing any of the other supported types, and sends the whole array as the `arguments` to your JavaScript callback.
* Quirk: this is not just syntactic sugar (though it is sweet). This way, all of the arguments are serialized/deserialized as necessary. e.g. it is safe to return `NSData*` as multipart, but not as `Array`/`Dictionary`.
## Plugin Signatures
The **new signature** supported beginning in **Cordova 2.1.0** is:
- (void)myMethod:(CDVInvokedUrlCommand*)command;
The **old (deprecated)** signature is:
- (void)myMethod:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
Basically, the options dictionary has been removed for the new signature, and the callbackId is not the 0th index item for the arguments array, but it is now in a separate property.
## Echo Plugin iOS Plugin
We would add the following to the `<plugins>` tag of the project's `config.xml` file:
<plugin name="Echo" value="Echo" />
Then we would add the following files (`Echo.h` and `Echo.m`) to the Plugins folder inside our Cordova-iOS
application folder:
/********* Echo.h Cordova Plugin Header *******/
#import <Cordova/CDV.h>
@interface Echo : CDVPlugin
- (void)echo:(CDVInvokedUrlCommand*)command;
@end
/********* Echo.m Cordova Plugin Implementation *******/
#import "Echo.h"
#import <Cordova/CDV.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
Let's take a look at the code. At the top we have all of the necessary
Cordova imports. Our class extends from `CDVPlugin` - very important.
This plugin only supports one action, the `echo` action. First, we
grab the echo string using the `objectAtIndex` method on our `args`,
telling it we want to get the 0th parameter in the arguments array. We
do a bit of parameter checking: make sure it is not `nil`, and make
sure it is not a zero-length string.
If it is, we return a `PluginResult` with an `ERROR` status. If all of
those checks pass, then we return a `PluginResult` with an `OK`
status, and pass in the `echo` string we received in the first place
as a parameter.
Finally, we send the result to `self.commandDelegate`, which executes
the `exec` method's success or failure callbacks on the JavaScript
side. If the success callback is called, it passes in the `echo`
parameter.
## Threading
Plugin methods are executed in the same thread as the UI. If your
plugin requires a great deal of processing or requires a blocking
call, you should use a background thread. For example:
- (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];
}];
}
## Advanced Plugin Functionality
See other methods that you can override in:
1. [CDVPlugin.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/CDVPlugin.h)
2. [CDVPlugin.m](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/CDVPlugin.m)
For example, you can hook into the `pause`, `resume`, app terminate and `handleOpenURL` events.
## Debugging Plugins
To debug the Objective-C side, you would use Xcode's built-in debugger.
For JavaScript, on iOS 5.0 you can use
[Weinre, an Apache Cordova Project](https://github.com/apache/cordova-weinre) or
[iWebInspector, a third-party utility](http://www.iwebinspector.com/)
For iOS 6, you would use Safari 6.0 to simply attach to your app
running in the iOS 6 Simulator.
## Common Pitfalls
* Don't forget to add your plugin's mapping to config.xml. If you forget, an error is logged in the Xcode console.
* Don't forget to add any hosts you connect to in the [whitelist](guide_whitelist_index.md.html#Domain%20Whitelist%20Guide). If you forget, an error is logged in the Xcode console.
## Deprecated Plugin Signature Note
The **old (deprecated)** signature is:
- (void) myMethod:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
The Objective-C plugin method's `options` parameter is deprecated, and
should not be used. For legacy reasons, the last JavaScript object
passed in the `args` array is passed in as the `options` dictionary of
the Objective-C method. You must make sure that any JavaScript object
that is passed in as an element in the `args` appears as the last item
in the array. Otherwise it throws off the array index for all
subsequent parameters of the Objective-C array. Only one JavaScript
object is supported for the options dictionary, and only the last one
encountered will be passed to the native method. Errors stemming from
this problem are the reason `options` has been deprecated.