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


はじめに

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

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

突然ですが、Cordova って辛くないですか?

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

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

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

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

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

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

The JavaScript Interface より引用

詳しくはこちらの記事を参照してください。

この、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 より引用

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

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

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

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

逆にデメリットとしては、 @angular/core が必要となることでしょう。
これは、Ionic Native で @Injectable デコレータが利用されているためです。

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

Using in React project · Issue #2687 · ionic-team/ionic-native

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

それでは、Ionic Natvie の Geolocation プラグインを Non-Angular アプリケーションから利用してみます。

Cordova プロジェクトの作成

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

プラットフォームの追加

$ cordova platform add ios
$ cordova platform add android

環境構築

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

$ npm install babel-core babel-loader babel-preset-env webpack webpack-cli --save-dev
.babelrc
{
  "presets": [
    [
      "env", {
        "modules": false
      }
    ]
  ]
}
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 を利用するために必要なパッケージをインストールします。

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

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

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

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

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 で読み込むように修正します。

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 で記述します。

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);

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

$ npm run webpack
$ cordova run ios
$ cordova run android

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

kotarella1110/cordova-with-ionic-native: Use Ionic Native in Apache Cordova

最後に

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

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