Cordova プラグインのコールバック地獄から脱却！Non-Angular アプリケーションでも Ionic Native を利用するという選択

## はじめに

Ionic ユーザー向けというより Cordova ユーザー向けの記事です。

筆者は、諸事情により Cordova を利用してモバイルアプリケーションを開発しています。
WebView 内の SPA は Riot.js だったり React だったりです。

突然ですが、**Cordova って辛くないですか？**笑

何が辛いか挙げていくとキリがないので今回は省略しますが、
そんな辛さの一つを Ionic を利用して解決出来ますよというお話です。

## Cordova プラグインのコールバック地獄

Cordova アプリケーションを開発している方々には理解して頂けると思います...。

基本的に Cordova プラグインは、引数に成功時・失敗時のコールバック関数を受け取るようになっています。
そのため、Cordova アプリケーションは**コールバック地獄**になりがちです。

これは、ネイティブプラットフォームと通信するための `cordova.exec` が引数に成功時・失敗時のコールバック関数を受け取るようになっていることに起因しています。

```js
cordova.exec(function(winParam) {},
             function(error) {},
             "service",
             "action",
             ["firstArgument", "secondArgument", 42, false]);
```

> [The JavaScript Interface](https://cordova.apache.org/docs/en/latest/guide/hybrid/plugins/index.html#the-javascript-interface) より引用

詳しくは[こちらの記事](http://neos21.hatenablog.com/entry/2017/09/07/080000)を参照してください。

この、Cordova プラグインによるコールバック地獄から脱却するにはどうすれば良いでしょうか？
Cordova プラグインを Promise でラップしてあげれば脱却できそうですね。
しかし、Cordova プラグインをインストールする度にラッパーなんて作るの面倒・・・。

そんな方々に Ionic Native の利用をお勧めします！
Ionic Native 利用できるのは Angular アプリケーションだけでしょ？と思った方々も安心して下さい。
React や Vue アプリケーション等の、 Non-Angular な Cordova アプリケーションからでも利用することが可能です。

## Ionic Native を利用するという選択

Ionic Native の Overview には以下のような記載がされています。

> **Ionic Native is a TypeScript wrapper for Cordova/PhoneGap plugins** that make adding any native functionality you need to your Ionic mobile app easy.

> **Ionic Native wraps plugin callbacks in a Promise or an Observable**, providing a common interface for all plugins and ensuring that native events trigger change detection in Angular.

> [Ionic Native - Ionic Native](https://ionicframework.com/docs/native/) より引用

つまり、**Ionic Native は Promise または Observable で Cordova/PhoneGap プラグインをラップした TypeScript ラッパーです。**

そのため、以下のようなメリットを享受することが可能です。

* **Promise または Observable によるコールバック地獄からの脱却**
* **TypeScript による型の恩恵**

TypeScript で記述していればコールバック地獄からの脱却に加え、型の恩恵も享受することができます。

逆にデメリットとしては、 [`@angular/core`](https://www.npmjs.com/package/@angular/core) が必要となることでしょう。
これは、Ionic Native で `@Injectable` デコレータが利用されているためです。

以下の機能要求が受諾されれば、Non-Angular アプリケーションでは `@angular/core` は不要になるかもしれません。

[Using in React project · Issue #2687 · ionic-team/ionic-native](https://github.com/ionic-team/ionic-native/issues/2687)

## Non-Angular アプリケーションからの Ionic Native の利用

それでは、[Ionic Natvie の Geolocation プラグイン](https://ionicframework.com/docs/native/geolocation/)を Non-Angular アプリケーションから利用してみます。

### Cordova プロジェクトの作成

```shell
$ cordova create cordova-with-ionic-native
$ cd cordova-with-ionic-native/
```

### プラットフォームの追加

```shell
$ cordova platform add ios
$ cordova platform add android
```

### 環境構築

webpack と Babel で環境を構築します。

```
$ npm install babel-core babel-loader babel-preset-env webpack webpack-cli --save-dev
```

```json:.babelrc
{
  "presets": [
    [
      "env", {
        "modules": false
      }
    ]
  ]
}
```

```js:webpack.config.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: './www/js/index.js',
  output: {
    path: path.resolve(__dirname, 'www/js'),
    filename: 'index.bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
    ],
  },
  devtool: 'source-map',
};
```

### パッケージとプラグインのインストール

Ionic Native を利用するために必要なパッケージをインストールします。

```shell
$ npm install @angular/core @ionic-native/core rxjs --save
```

Geolocation プラグインをインストールします。

```shell
$ cordova plugin add cordova-plugin-geolocation --variable GEOLOCATION_USAGE_DESCRIPTION="To locate you"
$ npm install --save @ionic-native/geolocation
```

iOS の場合は `config.xml` を修正する必要があります。

```diff:config.xml
     <platform name="ios">
         <allow-intent href="itms:*" />
         <allow-intent href="itms-apps:*" />
+        <edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
+            <string>need location access to find things nearby</string>
+        </edit-config>
     </platform>
```

### アプリケーションの修正

webpack によってバンドルされた JavaScript ファイルを HTML で読み込むように修正します。

```diff:www/js/index.html
-         <script type="text/javascript" src="js/index.js"></script>
+         <script type="text/javascript" src="js/index.bundle.js"></script>
```

Ionic Natvie の Geolocation プラグインを呼び出し、Promise で記述します。

```js:www/js/index.js
import { Geolocation } from '@ionic-native/geolocation';

document.addEventListener('deviceready', () => {
  // onSuccess Callback
  // This method accepts a Position object, which contains the
  // current GPS coordinates
  //
  const onSuccess = position => {
    alert([
      `Latitude: ${position.coords.latitude}`,
      `Longitude: ${position.coords.longitude}`,
      `Altitude: ${position.coords.altitude}`,
      `Accuracy: ${position.coords.accuracy}`,
      `Altitude Accuracy: ${position.coords.altitudeAccuracy}`,
      `Heading: ${position.coords.heading}`,
      `Speed: ${position.coords.speed}`,
      `Timestamp: ${position.timestamp}`,
    ].join('\n'));
  };

  // onError Callback receives a PositionError object
  //
  const onError = error => {
    alert([
      `code: ${error.code}`,
      `message: ${error.message}`,
    ].join('\n'));
  }

  const geolocation = new Geolocation();
  geolocation.getCurrentPosition()
    .then(onSuccess)
    .catch(onError);
}, false);
```

実行して確認して見て下さい。
問題なく動作します。

```shell
$ npm run webpack
$ cordova run ios
$ cordova run android
```

サンプルは GitHub 上で公開しています。

[kotarella1110/cordova-with-ionic-native: Use Ionic Native in Apache Cordova](https://github.com/kotarella1110/cordova-with-ionic-native)

## 最後に

Ionic Native 然り、Ionic いいですよね...。Cordova の足りない部品や機能全てが整っている感じがします。
Ionic v4 から UI コンポーネントが Web Components ベースになり、 Non-Angular アプリケーションでも利用できるとのことで大変嬉しく思います。
Ionic CLI は Non-Angular アプリケーションでも利用できるようになったりしませんかね？
標準でライブリロードをサポートしていたり、Cordova CLI より API が充実していて魅力的です...。

Ionic team が [Capacitor](https://capacitor.ionicframework.com/) という Cordova からインスピレーションを得たクロスプラットフォームを開発しているらしいですね。
こちらにも今後注目して行きたいです。