How to create a custom Cordova plugin in 2024
Developing for IONIC with Cordova or directly in Cordova is a fast way to get a mobile application, the time to develop and the time to market is faster than developing a native App for Android and IOS. But when you need a native feature and you do not find a plugin made by the community that fits into your needs is a headache.
The first thing you could do is check the Cordova documentation, which is not clear at all. In this blog, I will explain step by step how I created a custom Cordova plugin in 2024. For this blog, I suppose that you already know about native development, it is important, because, you will have to write Android native code(Java) and IOS native code (Swift).
Use PlugMan to create the base
1. Install Plugman Globally:
- Command:
npm install -g plugman
This installs the Plugman utility globally on your system, enabling you to manage Cordova plugins from the command line.
2. Create the Plugin Structure:
- Command:
plugman create --name CordovaHelloWorld --plugin_id com-kalex-plugings-helloWorld --plugin_version 1.0.0 --path ./cordovaPlugin
plugman create
: Initiates plugin creation.--name CordovaHelloWorld
: Assigns the plugin a friendly name.--plugin_id com-kalex-plugings-helloWorld
: Sets a unique identifier for the plugin.--plugin_version 1.0.0
: Specifies the initial version number.--path ./cordovaPlugin
: Designates the directory where the plugin structure will be generated.
This command constructs the basic plugin structure with essential files and folders within the specified path.
3. Add Platform Support:
- Command (Android):
plugman platform add --platform_name android
- Command (iOS):
plugman platform add --platform_name iOS
These commands enable the plugin to function on the specified platforms (Android and iOS in this case). They generate platform-specific code and configuration within the plugin’s structure.
Understand the base of Cordova plugin development
Key Components: A Cordova plugin typically consists of:
- JavaScript Interface: A JavaScript file (or files) that provides a JavaScript API for developers to interact with the plugin’s functionality from within their Cordova app.
- Platform-Specific Code: Native code (e.g., Java for Android, Objective-C for iOS) that implements the actual device interactions for each supported platform. This code resides in platform-specific subdirectories within the plugin structure.
- Plugin.xml Metadata: A file containing essential information about the plugin, such as its name, ID, version, supported platforms, JavaScript files, and native code files.
Going deep into the native Cordova classes
As you can see in the native part, we have the inheritance of specific classes from the Cordova project, in Android we extends from CordovaPlugin, and in IOS we extends from CDVPlugin.
But, what do these classes bring to the table?
The Android side, gives us access to the webView ,
Cordova and preferences.
As well, we have access to control the lifecycle methods of the activity
We can override these methods in our plugin class to control events of the life cycle in Android.
On the other hand, the IOS side gives us access to a webView, webViewEngine, and viewController. We also have access to some lifecycle methods.
- (void)onAppTerminate;
- (void)onMemoryWarning;
- (void)onReset;
-(void)dispose;
but the good ones come when using the Notification Center
So, with these methods we have access to the Activity and ViewController on each side, as well as access to the most common and important lifecycle methods in native development.
Write the JS bride to communicate with IONIC
In resume, this is the code for our JS bride
var exec = require("cordova/exec");
exports.sayHello = function (arg0, success, error) {
exec(success, error, "CordovaHelloWorld", "sayHello", [arg0]);
};
exports.enable = function (success, error) {
exec(success, error, "CordovaHelloWorld", "enable");
};
exports.disable = function (success, error) {
exec(success, error, "CordovaHelloWorld", "disable");
};
lest break down this one to understand what is happening.
1. Importing the exec
Function:
var exec = require("cordova/exec");
- The
exec
function is the primary way for JavaScript code in a Cordova app to communicate with native platform code (e.g., Java on Android or Objective-C on iOS).
2. Exporting Plugin Functions:
exports.sayHello = function (arg0, success, error) { ... }
- This line defines a function named
sayHello
, exports it, and makes it accessible to other parts of your Cordova app. - The function takes three arguments:
arg0
: An argument that will be passed to the native code.success
: A callback function that will be invoked if the native code execution is successful.error
: A callback function that will be invoked if there's an error during the native code execution.
3. Executing Native Code:
exec(success, error, "CordovaHelloWorld", "sayHello", [arg0]);
- This line calls the
exec
function to execute native code. - The arguments passed to
exec
are: success
: The callback function to be invoked if the native code execution is successful.error
: The callback function to be invoked if there's an error.- “CordovaHelloWorld”: The name of the class that inheritance from Cordova (The classes we talk in the previous part ).
- “sayHello”: The specific method within the native plugin that you want to call.
[arg0]
: An array containing any arguments that need to be passed to the native method (in this case, thearg0
argument from the JavaScript function).
Write the Native code
Before starting with the code, my advice to create the native part is first to create the feature in a native project, test the native feature, and then paste the code to the plugin, including the imports.
Android code 🤖
Again and to resume the post :) , this is the code in the Android side.
public class CordovaHelloWorld extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("sayHello")) {
String message = args.getString(0);
this.sayHello(message, callbackContext);
return true;
}
return false;
}
private void sayHello(String message, CallbackContext callbackContext) {
if (message != null && message.length() > 0) {
Toast.makeText(webView.getContext(), message, Toast.LENGTH_LONG).show();
callbackContext.success(message);
} else {
callbackContext.error("Expected one non-empty string argument.");
}
}
}
Now, lest break down the code.
1. Class Definition:
public class CordovaHelloWorld extends CordovaPlugin { ... }
- The
CordovaPlugin
class is the base class for custom Cordova plugins on Android, providing the structure for interacting with JavaScript code.
2. execute
Method:
@Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { ... }
- This method is called when JavaScript code invokes a method from the plugin.
- It receives:
action
: The name of the method being called.args
: JSONArray of arguments passed from JavaScript.callbackContext
: A context object used to send results or errors back to JavaScript.- It checks the
action
and, if it's "sayHello", it calls thesayHello
method.
3. sayHello
Method:
private void sayHello(String message, CallbackContext callbackContext) { ... }
- This method displays a Toast message with the provided
message
. - It checks if the message is valid and either:
- Calls
callbackContext.success(message)
to send a success response back to JavaScript. - Calls
callbackContext.error(...)
to send an error response.
IOS code 🍏
By default the Cordova plugin is in Objective-c, but we can use Swift in our custom plugin, we need to add the famous notation @objc() and we need a kind of “bride”, called Bridging-Header.h Fortunately now day in Cordova 11 this file is generated automatically :) , lest go with the native code.
/********* CordovaHelloWorld.m Cordova Plugin Implementation *******/
import Foundation
import Cordova
import UIKit
@objc(CordovaHelloWorld)
class CordovaHelloWorld: CDVPlugin {
@objc func sayHello(_ command: CDVInvokedUrlCommand) {
var pluginResult = CDVPluginResult(
status: CDVCommandStatus_ERROR
)
let msg = command.arguments[0] as? String ?? ""
if msg.characters.count > 0 {
let toastController: UIAlertController =
UIAlertController(
title: "",
message: msg,
preferredStyle: .alert
)
self.viewController?.present(
toastController,
animated: true,
completion: nil
)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
toastController.dismiss(
animated: true,
completion: nil
)
}
pluginResult = CDVPluginResult(
status: CDVCommandStatus_OK,
messageAs: msg
)
}
self.commandDelegate!.send(
pluginResult,
callbackId: command.callbackId
)
}
}
1. Imports and Class Definition:
@objc(CordovaHelloWorld) class CordovaHelloWorld: CDVPlugin { ... }
: Defines an Objective-C class namedCordovaHelloWorld
that extends theCDVPlugin
class, the base class for Cordova plugins on iOS.
2. sayHello
Method:
@objc func sayHello(_ command: CDVInvokedUrlCommand) { ... }
: This method is called when JavaScript invokes the "sayHello" method from the plugin.var pluginResult = CDVPluginResult(status: CDVCommandStatus_ERROR)
: Initializes aCDVPluginResult
object to send a response back to JavaScript, initially set to an error status.
3. Handling Message and Displaying Alert:
let msg = command.arguments[0] as? String ?? ""
: Retrieves the first argument passed from JavaScript (the message to display) and safely handles potential missing arguments.if msg.characters.count > 0 { ... }
: If the message is not empty:- Creates a
UIAlertController
to display as a temporary alert. - Presents the alert controller on the main view controller.
- Uses
DispatchQueue.main.asyncAfter
to dismiss the alert automatically after 3 seconds. - Updates the
pluginResult
to a success status with the message.
4. Sending Response to JavaScript:
self.commandDelegate!.send(pluginResult, callbackId: command.callbackId)
: Sends thepluginResult
back to JavaScript, indicating success or error and potentially returning the message.
Create the package.json
Here just run this command and Plugman will create it for you :)
sudo plugman createpackagejson "url to your plugin folder"
Create the IONIC wrapper
Now it's time to run our custom lib, to do this we gonna use the Awesome-Cordova-plugins repo, and clone it in your Desktop folder (for example).
git clone https://github.com/danielsogl/awesome-cordova-plugins.git
then run these commands, were “Cordova HelloWorld” is the name of the plugin
sudo npm install
gulp plugin:create -m -n CordovaHelloWorld
go to awesome-cordova-plugins/src/@awesome-cordova-plugins/plugins and search the folder that matches your plugin.
open the index.ts and edit it, the file is already well explained and documented, however, this is an example.
The next step is to build the Awesome-Cordova-plugins
npm run build
this will generate a folder dist
we just need to copy the folder of our plugin and paste it into the Awesome-Cordova-plugins in our project, With the image you will have a good reference.
Naturally, this manual way is not the best for production, in a professional environment, this IONIC wrapper generation is made by a pipeline and added to the ionic-native automatically when we install the dependencies in IONIC, but that is another topic.
Add your new plugin to the project
sudo ionic cordova plugin add <path to the plugin>
add the plugin and declare the plugin in the AppModule
now you can use the plugin in any component !!
Test your new Cordova plugin
Run your IONIC app sudo ionic cordova run android or sudo ionic cordova run ios
There it is, congratulations 👏 , you have created a new Cordova plugin.
Thanks for reading I hope the article was enjoyable, useful and informative for you. You can reach me on LinkedInd and YouTube. Do not forget to follow me and like the post.