Cordovaアプリケーション開発に、NoSQLドキュメント指向組み込みデータベースCouchbase Liteを使うには？（iOSネイティブプラグイン構築手順）

## はじめに

昨年末に、NoSQLドキュメント指向組み込みデータベースCouchbase Liteを用いたモバイルアプリを開発する際に、iOSとAndroidの両方で利用するために、クロスプラットフォーム/ハイブリッドアプリ（※）開発に関連する記事をいくつか発表しました（*「クロスプラットフォーム」と「ハイブリッド」という言葉はそれぞれ異なる意味を持つはずですが、区別せずに使っている例もまま見られ、ここでは一つの同じ目的を持つカテゴリーを指す言葉として利用しています）。


React Nativeでは、JavaScriptやTypeScriptで書かれたコードが、JavaScriptエンジンで実行されます。

https://qiita.com/yoshiyuki_kono/items/f06a59a56738da4cd0a0

Flutterでは、Dartで書かれたコードが、C/C++コードとしてコンパイルされて、ネイティブに実行されます。

https://qiita.com/yoshiyuki_kono/items/d253caba38a9b12ba8f3

Xamarinでは、C#で書かれたコードが、Intermediate Language（IL)としてコンパイルされます。

https://qiita.com/yoshiyuki_kono/items/1c60f3a91d483b5ed133

本稿では、Apache Cordobaでのケースについて、整理します。

https://cordova.apache.org/

そもそもの話として、Couchbaseをモバイルアプリケーションで利用する意義については、以下の記事をご参考ください。

https://qiita.com/yoshiyuki_kono/items/3abd5e32801060a449d2

### Cordovaとは？
JavaScript、HTML、CSSといったウェブアプリケーション開発技術を使って、モバイルアプリケーションを開発することができるフレームワーク。これらのウェブアプリケーション開発技術はモバイルデバイスのブラウザビュー上で動作する形になる。加えて、モバイルデバイスのAPIにアクセスすることができるため、カメラ、GPS、加速器センサーなどを使った、モバイルアプリケーションを開発することができる。

### PWA(Progressive Web Apps)とは？

WebサイトやWebアプリをネイティブアプリのようにインストール可能にする技術

### Ionicとは？

PWA(Progressive Web Apps)を開発するためのフレームワーク。Cordovaとの組み合わせで、ネイティブアプリとしてデプロイするオプションを持つ。


## アーキテクチャー

Cordovaで、Couchbase　Liteを使用する場合、Cordova Webアプリケーション内からCouchbase LiteのネイティブAPIにアクセスする必要があります。

Cordovaプラグインを使用してネイティブコードライブラリにアクセスするCordovaアプリケーションのアーキテクチャ-概念図を以下に示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/b9a2bc36-8cd9-82a6-815d-de6fe7adda5b.png)

- UIレイヤーまたはWebアプリレイヤーはHTML / Javascriptを使用して記述されており、モバイルプラットフォーム間で共通です。
- WebViewはWebアプリのレンダリングに使用されます
- Couchbase Lite Cordovaプラグインは、ネイティブプラットフォーム言語で記述されており、Couchbase Liteとのインターフェイスを担当します。プラグインは、Webアプリからアクセス可能なJavaScript APIを公開します。
- Couchbase Liteは、OSネイティブ環境で動作します。Webアプリはプラグインを使用してネイティブ機能にアクセスします。



## チュートリアル紹介

Couchbase公式チュートリアルとして下記があります。

https://docs.couchbase.com/tutorials/hotel-lister/ios.html

このチュートリアルでは、iOSでCouchbase Lite2.xとインターフェイスするCordovaプラグインを構築する方法について説明しています。このチュートリアルでは、フロントエンドWebアプリレイヤーの構築にIonicを使用しています。

チュートリアルでは、Couchbase Liteデータベースに保存されているホテルに関する情報を表示および検索するサンプルアプリケーションが用いられています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/07536be7-7be6-9c2a-5d4c-39130f239177.png)


### 前提条件
このチュートリアルは、次のコンポーネントとバージョンを前提としています。

- Xcode10
- Swift 4.2
- Couchbase Lite 2.1.1

上記からも分かるように、このチュートリアルは発表時点のテクノロジースタックを元にしています。

開発を始め際には、前提となるフレームワーク、IonicとCordovaの準備が必要になりますが、チュートリアルが提供するサンプルプロジェクトは、過去のバージョンが用いられています。

本稿では、チュートリアルのサンプルアプリそのものの構築からは離れて、Couchbase Liteプラグイン構築手順に焦点を当てて解説します。また、IonicおよびCordovaのインストール手順やプロジェクト実行方法についても、割愛します。

#### Couchbase Liteのセットアップ

Xcodeプロジェクトの依存関係としてCouchbase Liteをインポートします。

Couchbase Liteは、以下のURLからダウンロードできます。

https://www.couchbase.com/downloads

以下の記事で、Carthageを使用して、Couchbase Liteをセットアップする手順を紹介しています。

上記URLからダウンロードした場合も、Xcodeプロジェクトに追加する部分では、共通の手順となります。

https://qiita.com/yoshiyuki_kono/items/efe00cd3add47d09efee

また、以下の記事で、Swiftパッケージマネージャーを使用して、Couchbase Liteをセットアップする手順を紹介しています。

https://qiita.com/yoshiyuki_kono/items/9d9988826014bfc331d3



## iOSネイティブプラグイン構築手順

Cordovaプラグインを使うと、JavaScriptからネイティブコードに、アクセスすることができます。プラグインは、通常Webベースのアプリでは利用できないデバイスおよびプラットフォーム機能へのアクセスを提供します。

ここでは、Couchbase LiteのSwift APIを使用してcordova-plugin-hotel-listerというプラグインを作成する例を紹介します。

### プラグイン定義ファイル


#### plugin.xml

次のように、plugin.xmlを作成します。

```XML:plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
        xmlns:android="http://schemas.android.com/apk/res/android"
        id="cordova-plugin-hotel-lister" version="0.0.1">
  <name>cordova-plugin-hotel-lister</name>
  <description>An Cordova plugin for the Hotel Lister application to implement data persistence using Couchbase Lite.</description>
  <license>Couchbase CE / https://www.couchbase.com/binaries/content/assets/website/legal/ce-license-agreement.pdf</license>

  <engines>
    <engine name="cordova" version=">=3.0.0"/>
  </engines>

  <js-module src="www/hotel-lister.js" name="HotelLister">
    <clobbers target="window.plugins.HotelLister" />
  </js-module>

  <platform name="ios">
    <config-file target="config.xml" parent="/*">
      <feature name="HotelLister">
        <param name="ios-package" value="HotelLister"/>
      </feature>
    </config-file>

    <source-file
      src="src/ios/HotelLister.swift"/>
  </platform>
</plugin>
```

- `id="cordova-plugin-hotel-lister"`属性は、後に使用されるIonicのプロジェクトでそれをインポートするプラグインの名前です。
- XMLタグ`<js-module></js-module>`は、JavaScriptのインタフェースを宣言しているファイルの場所を示します。
- XMLタグ`<platform name="ios"></platform>`は、ネイティブコードを保持するためのSwiftファイルの場所を宣言します。

#### package.json

次のように、package.jsonを作成します。

```json:package.json
{
  "name": "cordova-plugin-hotel-lister",
  "version": "0.0.1",
  "description": "A Cordova plugin for the Hotel Lister app.",
  "cordova": {
    "id": "cordova-plugin-hotel-lister",
    "platforms": [
      "android",
      "ios"
    ]
  },
  "engines": [
    {
      "name": "cordova",
      "version": ">=3.0.0"
    }
  ],
  "author": "Couchbase <mobile@couchbase.com>",
  "license": "Couchbase CE / https://www.couchbase.com/binaries/content/assets/website/legal/ce-license-agreement.pdf"
}
```

### API定義ファイル

Cordova/Ionic Webアプリがプラグインと対話するためのJavaScript APIを定義します。

このことで、プラグインが`window`要素にバインドされます。そうすることで、単純なJavaScript参照を介してアクセスが許可されます。

ここでは、以下のように、hotel-lister.jsという名称のファイルを作成します。

```JAVASCRIPT:hotel-lister.js
var exec = require('cordova/exec');

var hotelLister = {
  /* code will be added here later */
};

module.exports = hotelLister;
```

###  ネイティブソース雛形作成

次のように、HotelLister.swiftを作成します。
このファイルは、hotel-lister.jsで定義されているAPIに対応するネイティブのSwift実装を提供します。

```swift:HotelLister.swift
@objc(HotelLister) class HotelLister : CDVPlugin {

    override func pluginInitialize() {

    }
}
```

Cordovaプラグインは、JavaScipt WebViewアプリで最初に呼び出されたときにインスタンス化されます。この`pluginInitialized()`メソッドにはスタートアップコードが含まれています。

詳細については、以下のプラグインライフサイクルのドキュメントを参照してください。

https://cordova.apache.org/docs/en/latest/guide/platforms/ios/plugin.html#plugin-initialization-and-lifetime

### プラグインインストール

プラグインが期待どおりに機能する準備ができていることを確認するには、プラグインをHotelListerプロジェクトにインストールする必要があります。

```BASH
cordova plugin add ../cordova-plugin-hotel-lister/
```

成功すると、出力に次のように表示されます。

```BASH
Adding cordova-plugin-hotel-lister to package.json
Saved plugin info for "cordova-plugin-hotel-lister" to config.xml
```


### 新しいネイティブソースの追加

データベース管理クラスを追加します。

以下のように、DatabaseManager.swiftを作成します。

```swift:DatabaseManager.swift
import CouchbaseLiteSwift

class DatabaseManager {

    private static var privateSharedInstance: DatabaseManager?

    var database: Database

    let DB_NAME = "travel-sample"

    class func sharedInstance() -> DatabaseManager   {
        guard let privateInstance = DatabaseManager.privateSharedInstance else {
            DatabaseManager.privateSharedInstance = DatabaseManager()
            return DatabaseManager.privateSharedInstance!
        }
        return privateInstance
    }

    private init() {
        let path = Bundle.main.path(forResource: self.DB_NAME, ofType: "cblite2")!
        if !Database.exists(withName: self.DB_NAME) {
            do {
                try Database.copy(fromPath: path, toDatabase: self.DB_NAME, withConfig: nil)
            } catch {
                fatalError("Could not copy database")
            }
        }
        do {
            self.database = try Database(name: "travel-sample")
        } catch {
            fatalError("Could not copy database")
        }
    }

}
```

このコードでは、まず「travel-sample」という名前のデータベースがデフォルトのCouchbase Liteディレクトリに存在するかどうかを確認します。存在しない場合は、アプリにバンドルされていたtravel-sample.cblite2データベースファイルをデフォルトのCouchbase Liteディレクトリにコピーされます。

このサンプルでは、事前にホテルのデータが登録されたCouchbase Liteデータベースを使っています。そのため、プロジェクトに、事前に構築されたデータベースファイルが追加されていることが前提となります。


#### plugin.xmlの`<platform/>`要素編集

新しいSwiftファイルを追加するときは、プラグイン構成ファイルに参照を追加する必要があります。
以下の要素を、plugin.xmlの`<platform name="ios"></platform>`要素に挿入します。

```XML:plugin.xml
<source-file
	src="src/ios/DatabaseManager.swift"/>
```

### ネイティブソース実装追加

次のインポートステートメントをHotelLister.swiftの先頭に追加します。

```swift:HotelLister.swift
import CouchbaseLiteSwift
```

次のインスタンス変数をHotelLister.swiftのHotelListerクラスに追加します。

```swift:HotelLister.swift
var database: Database?
```

次に、`HotelLister.swiftの`pluginInitialize()`メソッドを次のように更新して、`database`インスタンスプロパティを設定します。

```swift:HotelLister.swift
override func pluginInitialize() {
    self.database = DatabaseManager.sharedInstance().database
}
```

さらに、データベースからホテルを一覧表示する機能を追加します。

`queryHotels`メソッドの実装をHotelLister.swiftに追加します。

```swift:HotelLister.swift
@objc(queryHotels:)
func queryHotels(command: CDVInvokedUrlCommand) {
    let DOC_TYPE = "hotel";

    let query = QueryBuilder
        .select(
            SelectResult.expression(Meta.id),
            SelectResult.property("address"),
            SelectResult.property("phone"),
            SelectResult.property("name")
        )
        .from(DataSource.database(database!))
        .where(
            Expression.property("type")
                .equalTo(Expression.string(DOC_TYPE))
    )
    
    do {
        let resultSet = try query.execute()
        let resultSetArray = resultSet.allResults()
        var array = [Any]()
        for item in resultSetArray {
            array.append(item.toDictionary())
        }
        let pluginResult = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: array)
        self.commandDelegate.send(pluginResult, callbackId: command.callbackId)
    } catch {
        fatalError(error.localizedDescription);
    }
}
```

次に、プラグインのJavaScriptインターフェースを更新します。hotel-lister.jsに次のコードを追加します。

```JAVASCRIPT:hotel-lister.js
queryHotels: function(successCallback, errorCallback) {
  exec(successCallback, errorCallback, 'HotelLister', 'queryHotels');
},
```

これで、Ionicプロジェクトで`queryHotels`メソッドを呼び出すことができます。home.tsに以下を追加します。

```JAVASCRIPT:home.ts
this.platform.ready().then(() => {
  window.plugins.HotelLister.queryHotels((hotels) => {
    console.log(hotels);
    this.hotels = hotels;
  });
});
```

### Cordovaプラグイン再インストール

変更を反映するには、Cordovaプラグインを再インストールする必要があります。


プロジェクトをビルドし、変更を確認します。

## 最後に

本稿では、Cordovaアプリケーション開発のために、iOS用のCouchbase Liteネイティブプラグインを構築する手順について解説しました。

Android用のCouchbase Liteネイティブプラグインを構築する手順について、以下で解説しています。

https://qiita.com/yoshiyuki_kono/items/39420e3c6ef47a8036ec


Couchbase Liteについての記事を以下の投稿で整理していますので、ご関心に応じて、参照してみてください。

https://qiita.com/yoshiyuki_kono/items/6a5bbad1b02196a25417

Couchbase Liteは、Couchbase Serverとの自動双方向同期が可能です。

Couchbase Serverについては、日本語で読むことができるまとまった情報として、次の拙著を紹介させていただきます。

https://www.amazon.co.jp/-/en/%E6%B2%B3%E9%87%8E-%E6%B3%B0%E5%B9%B8/dp/429560030X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1636588872&sr=1-1

## 関連情報

https://blog.couchbase.com/couchbase-lite-data-storage-ionic-app-cordova-plugins/

https://docs.couchbase.com/tutorials/hotel-lister/ios.html