Writing an Ionic Native Wrapper

September 8th 2017 Ionic Framework Cordova TypeScript

Ionic Native makes it very convenient to use Cordova plugins from Ionic applications. Although Ionic Native provides an impressive collection of wrappers for many Cordova plugins, there are still Cordova plugins out there without a corresponding plugin. In such cases you will need to either use them directly without a wrapper or write your own wrapper. The latter option is much simpler than you might think.

Using the Plugin Directly

WifiWizard is a Cordova plugin that's not yet supported by Ionic Native. It can be used to get the SSID of the Wi-Fi network that your Android or iOS device is currently connected to, and provides many other functions for Wi-Fi management on Android devices.

Actually, it's not all that difficult to invoke the plugin function from an Ionic application even without a wrapper:

WifiWizard.getCurrentSSID(
    ssid => this.ssid = ssid,
    error => console.log(error)
);

Since there are no TypeScript type definitions available for the plugin, you also need to add the following declaration at the top of each source file, where you're using it, or the code won't compile:

declare var WifiWizard;

That's already enough to make it work, but this approach has several disadvantages:

  • Without type declarations, the code editor can't auto-complete the code for you. You'll need to look up the available functions in plugin documentation.
  • There's no type safety at compile time. If the function signature is incorrect, the code will only fail at runtime.
  • Callbacks as function parameters are not idiomatic for Ionic code. Promises or Observables, which could be used instead, would make the code easier to read.
  • Direct plugin calls make testing difficult as they cannot be easily replaced with mocks.

Creating a Wrapper for the Plugin

The alternative is to write your own Ionic Native plugin. To make their lives easier, the authors of Ionic Native have written a lot of plumbing code. It might not be documented all that well, but it is distributed with the @ionic-native/core package and available for everyone to use.

This is a partial wrapper for the WifiWizard plugin, exposing only the getCurrentSSID function:

import { Injectable } from '@angular/core';
import { Plugin, IonicNativePlugin, Cordova } from '@ionic-native/core';

@Plugin({
    pluginName: 'WifiWizard', // should match the name of the wrapper class
    plugin: 'wifiwizard', // NPM package name
    pluginRef: 'WifiWizard', // name of the object exposed by the plugin
    repo: 'https://github.com/hoerresb/WifiWizard', // plugin repository URL
    platforms: ['Android', 'iOS'] // supported platforms
})
@Injectable()
export class WifiWizard extends IonicNativePlugin {

    @Cordova()
    getCurrentSSID(): Promise<string> { return; }

}

Ionic Native wrappers are implemented as Ionic providers that extend the IonicNativePlugin base class and require the Injectable decorator because of dependency injection. Additionally, the wrapped Cordova plugin must be described with the Plugin decorator. I explained the most important properties in the above code snippet. You can find a short description for other supported properties in source code comments.

Each exposed plugin function must be decorated with the Cordova decorator. For standard asynchronous functions, which have the success and error callbacks as the last two arguments, no additional properties are required in the decorator. The wrapper function should skip these two arguments and return a Promise instead, which will automatically map to the callback functions. The promise type should match the argument type of the success callback. The function body should consist of a simple return statement only.

If the callback arguments are at different positions, this can be specified with the successIndex and errorIndex properties of the Cordova decorator. For synchronous functions sync property must be set to true. All the available options are again only documented in the decorator source code. A very useful resource is also the source code for all existing Ionic Native wrappers.

Once you add the wrapper to the AppModule's providers collection, you're ready to rewrite your code that's calling the plugin:

this.wifiWizard.getCurrentSSID()
    .then(ssid => this.ssid = ssid)
    .catch(error => console.log(error));

Instead of adding the declare statement, you need to inject the provider in your class constructor:

constructor(wifiWizard: WifiWizard) { }

If you add JSDoc comments to the wrapper class, a good code editor will display them for you as well, so that you'll be much less likely to need the plugin documentation once your wrapper is ready.

Copyright
Creative Commons License