Cordovaに代わるCapacitorをIonic 4で試してみる

2019.02.21

Capacitorとは?

Capacitorとは、WebアプリをiOS・Android・Electron環境で動かすためのクロスプラットフォーム開発ライブラリ。プラットフォーム固有のAPIはiOSではSwiftで書かれた、AndroidはJavaで書かれたプラグインを使うことで限りなくWeb標準の書き方に近い形で呼び出すことができる。

2019年2月21現在、まだBeta版だが、ドキュメントも整っていたり国内で既にCapacitorとIonic 4を使った事例も生まれているようなので試してみることにした。

環境

  • macOS Mojave 10.14.2
  • Node.js v10.4.1
  • npm 6.8.0
  • Ionic CLI 4.10.3
  • Xcode 10.1
  • Android Studio 3.3.1

ソースコード

https://github.com/tetsushi-ito/ionic4-capacitor-sample

まずはIonic 4アプリケーションをIonic CLIを使って立ち上げる



$ ionic start

# プロジェクト名を指定
? Project name: Ionic4CapacitorSample

# テンプレートを選択
? Starter template:
  blank    | A blank starter project
  sidemenu | A starting project with a side menu with navigation in the content area
❯ tabs     | A starting project with a simple tabbed interface

# Appflowを使うか選択(今回は使わない)
? Install the free Ionic Appflow SDK and connect your app? (Y/n) n

出来上がったら、プロジェクトのディレクトリに移動してみる。



$ cd Ionic4CapacitorSample

この時点でのディレクトリ構成は以下の通り。



$ tree -I node_modules
.
├── angular.json
├── e2e
│   ├── protractor.conf.js
│   ├── src
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   └── tsconfig.e2e.json
├── ionic.config.json
├── package-lock.json
├── package.json
├── src
│   ├── app
│   │   ├── app-routing.module.ts
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── tab1
│   │   │   ├── tab1.module.ts
│   │   │   ├── tab1.page.html
│   │   │   ├── tab1.page.scss
│   │   │   ├── tab1.page.spec.ts
│   │   │   └── tab1.page.ts
│   │   ├── tab2
│   │   │   ├── tab2.module.ts
│   │   │   ├── tab2.page.html
│   │   │   ├── tab2.page.scss
│   │   │   ├── tab2.page.spec.ts
│   │   │   └── tab2.page.ts
│   │   ├── tab3
│   │   │   ├── tab3.module.ts
│   │   │   ├── tab3.page.html
│   │   │   ├── tab3.page.scss
│   │   │   ├── tab3.page.spec.ts
│   │   │   └── tab3.page.ts
│   │   └── tabs
│   │       ├── tabs.module.ts
│   │       ├── tabs.page.html
│   │       ├── tabs.page.scss
│   │       ├── tabs.page.spec.ts
│   │       ├── tabs.page.ts
│   │       └── tabs.router.module.ts
│   ├── assets
│   │   ├── icon
│   │   │   └── favicon.png
│   │   └── shapes.svg
│   ├── environments
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── global.scss
│   ├── index.html
│   ├── karma.conf.js
│   ├── main.ts
│   ├── polyfills.ts
│   ├── test.ts
│   ├── theme
│   │   └── variables.scss
│   ├── tsconfig.app.json
│   └── tsconfig.spec.json
├── tsconfig.json
└── tslint.json

Capacitorをプロジェクトに追加してみる



# 必要なパッケージをインストール
$ npm install --save @capacitor/core @capacitor/cli

# CLIで初期設定を行う
$ npx cap init

# アプリ名を指定
? App name Ionic4CapacitorSample

# アプリのパッケージIDを指定
? App Package ID (in Java package format, no dashes) net.playfulit.ionic4capacitorsample

✔ Initializing Capacitor project in /Users/tetsushi/projects/ionic/Ionic4CapacitorSample in 2.59ms

🎉   Your Capacitor project is ready to go!  🎉

Add platforms using "npx cap add":

  npx cap add android
  npx cap add ios
  npx cap add electron

Follow the Developer Workflow guide to get building:
https://capacitor.ionicframework.com/docs/basics/workflow

これで準備完了。プロジェクトはGit管理されているので、このコマンドによって発生した差分を確認してみると、プロジェクトのディレクトリの直下にcapacitor.config.jsonというファイルが生成されていた。



{
  "appId": "net.playfulit.ionic4capacitorsample",
  "appName": "Ionic4CapacitorSample",
  "bundledWebRuntime": false,
  "webDir": "www"
}

単に先ほどCLIから設定した情報が書かれているだけのようだ。

一度アプリをビルドする

iOSやAndroidをプラットフォームとして追加するためには、wwwというディレクトリが生成されている必要がある。一度アプリをビルドして、wwwを生成する。



# Ionicアプリのビルドを実行
$ ionic build

これでwwwというディレクトリにWebアプリケーション一式が出来上がった。

プラットフォームにiOSを追加してみる

続いて、アプリが動作するプラットフォームとしてiOSを追加してみる。



# プロジェクトのプラットフォームにiOSを追加
$ npx cap add ios

先ほどと同様にGitで変更を見てみると、プロジェクトのディレクトリの直下にiosというディレクトリが生成されていた。 ディレクトリ構造は以下の通り。ios/App/publicにはビルドしたWebアプリケーションのファイルが大量に入っているので除外した。



$ tree ios -I public
ios
├── App
│   ├── App
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets
│   │   │   ├── AppIcon.appiconset
│   │   │   │   ├── AppIcon-20x20@1x.png
│   │   │   │   ├── AppIcon-20x20@2x-1.png
│   │   │   │   ├── AppIcon-20x20@2x.png
│   │   │   │   ├── AppIcon-20x20@3x.png
│   │   │   │   ├── AppIcon-29x29@1x.png
│   │   │   │   ├── AppIcon-29x29@2x-1.png
│   │   │   │   ├── AppIcon-29x29@2x.png
│   │   │   │   ├── AppIcon-29x29@3x.png
│   │   │   │   ├── AppIcon-40x40@1x.png
│   │   │   │   ├── AppIcon-40x40@2x-1.png
│   │   │   │   ├── AppIcon-40x40@2x.png
│   │   │   │   ├── AppIcon-40x40@3x.png
│   │   │   │   ├── AppIcon-512@2x.png
│   │   │   │   ├── AppIcon-60x60@2x.png
│   │   │   │   ├── AppIcon-60x60@3x.png
│   │   │   │   ├── AppIcon-76x76@1x.png
│   │   │   │   ├── AppIcon-76x76@2x.png
│   │   │   │   ├── AppIcon-83.5x83.5@2x.png
│   │   │   │   └── Contents.json
│   │   │   ├── Contents.json
│   │   │   └── Splash.imageset
│   │   │       ├── Contents.json
│   │   │       ├── splash-2732x2732-1.png
│   │   │       ├── splash-2732x2732-2.png
│   │   │       └── splash-2732x2732.png
│   │   ├── Base.lproj
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── Info.plist
│   │   ├── capacitor.config.json
│   │   └── config.xml
│   ├── App.xcodeproj
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace
│   │   │   └── contents.xcworkspacedata
│   │   └── xcuserdata
│   │       ├── max.xcuserdatad
│   │       │   └── xcschemes
│   │       │       └── xcschememanagement.plist
│   │       └── tetsushi.xcuserdatad
│   │           └── xcschemes
│   │               └── xcschememanagement.plist
│   ├── App.xcworkspace
│   │   ├── contents.xcworkspacedata
│   │   ├── xcshareddata
│   │   │   └── IDEWorkspaceChecks.plist
│   │   └── xcuserdata
│   │       ├── max.xcuserdatad
│   │       │   └── UserInterfaceState.xcuserstate
│   │       └── tetsushi.xcuserdatad
│   │           └── UserInterfaceState.xcuserstate
│   ├── Podfile
│   ├── Podfile.lock
│   └── Pods
│       ├── Headers
│       ├── Local\ Podspecs
│       │   ├── Capacitor.podspec.json
│       │   └── CapacitorCordova.podspec.json
│       ├── Manifest.lock
│       ├── Pods.xcodeproj
│       │   ├── project.pbxproj
│       │   └── xcuserdata
│       │       └── tetsushi.xcuserdatad
│       │           └── xcschemes
│       │               ├── Capacitor.xcscheme
│       │               ├── CapacitorCordova.xcscheme
│       │               ├── Pods-App.xcscheme
│       │               └── xcschememanagement.plist
│       └── Target\ Support\ Files
│           ├── Capacitor
│           │   ├── Capacitor-dummy.m
│           │   ├── Capacitor-prefix.pch
│           │   ├── Capacitor-umbrella.h
│           │   ├── Capacitor.modulemap
│           │   ├── Capacitor.xcconfig
│           │   └── Info.plist
│           ├── CapacitorCordova
│           │   ├── CapacitorCordova-dummy.m
│           │   ├── CapacitorCordova-prefix.pch
│           │   ├── CapacitorCordova-umbrella.h
│           │   ├── CapacitorCordova.modulemap
│           │   ├── CapacitorCordova.xcconfig
│           │   └── Info.plist
│           └── Pods-App
│               ├── Info.plist
│               ├── Pods-App-acknowledgements.markdown
│               ├── Pods-App-acknowledgements.plist
│               ├── Pods-App-dummy.m
│               ├── Pods-App-frameworks.sh
│               ├── Pods-App-resources.sh
│               ├── Pods-App-umbrella.h
│               ├── Pods-App.debug.xcconfig
│               ├── Pods-App.modulemap
│               └── Pods-App.release.xcconfig
└── capacitor-cordova-ios-plugins
    ├── CordovaPlugins.podspec
    ├── CordovaPluginsResources.podspec
    ├── CordovaPluginsStatic.podspec
    ├── resources
    └── sources

これで準備完了。Xcodeを開くコマンドも用意されているので、それを使ってXcodeでプロジェクトを開いてみる。



$ npx cap open ios

Xcode上でとりあえずシミュレーターでiPhone XSを選択して実行ボタンを押してみると、あっさりシミュレーターでIonicアプリを動かすことができた。

Cordovaに代わるCapacitorをIonic 4で試してみる

プラットフォームにAndroidを追加してみる

続いて、アプリが動作するプラットフォームとしてAndroidを追加してみる。



# プロジェクトのプラットフォームにAndroidを追加
$ npx cap add android

Androidもこれで準備完了。iOSと同様にコマンドからIDEを起動できる。Androidの場合はAndroid Studioが起動する。



$ npx cap open android

自動でGradleのSyncが始まったが、以下のようなエラーが出て失敗してしまった。



ERROR: Failed to install the following Android SDK packages as some licences have not been accepted.
   platforms;android-27 Android SDK Platform 27
   build-tools;28.0.3 Android SDK Build-Tools 28.0.3
To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager.
Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html

Using Android SDK: /Users/tetsushi/Library/Android/sdk
Install missing SDK package(s)

Cordovaに代わるCapacitorをIonic 4で試してみる

「Install missing SDK package(s)」の部分が押せるようになっているので押してみる。

ライセンスを確認する画面が出てきたので、確認して次へ進む。

Cordovaに代わるCapacitorをIonic 4で試してみる

Cordovaに代わるCapacitorをIonic 4で試してみる

Android Studioでは、プラグインのアップデートが利用できる場合はそれを知らせてくれる。

Cordovaに代わるCapacitorをIonic 4で試してみる

Android Studioの初回のSyncはかなり時間がかかるので、気長に待つ必要がある。

準備ができたら実行ボタンを押す。エミュレーターにはあらかじめセットアップしていた「Nexus 5X API 23」を選択した。

おそらくAndroidアプリとしては正常に起動できたが、Webアプリケーションの部分でエラーが発生している模様。

Cordovaに代わるCapacitorをIonic 4で試してみる

(intermediate value).fill is not a functionというエラーが出ているので、ES6の機能を呼び出そうとしたらCapacitorが内部的に呼び出すAndroidのWebブラウザが古くて使えなかったという状況。

新しい機能を古い環境でも動作するようにパッチを当てるPolyfillsという仕組みがあるので、それを利用して対応することもできる。Ionic(Angular)プロジェクトでは、デフォルトでsrc/polyfills.tsというファイルが用意されていて、いくつかのPolyfillがテンプレートとして記述されている。

今回はAndroid Virtual Device Managerから「Pixel 2 API 28」を新たにセットアップしてそちらで試してみた。

Cordovaに代わるCapacitorをIonic 4で試してみる

API 28のAndroid端末では問題なく起動した。アプリ開発では特にWebアプリケーションを作るためのCSS・JavaScriptの対応バージョンや、Capacitor自体の対応バージョンをしっかり考えないといけない。Polyfillsを入れて対応するのか、サポートするOSバージョン・APIレベルを引き上げるのか慎重に検討したい。

Capacitorを使ったiOS/Android開発のワークフロー

Ionic 4とCapacitorを組み合わせて使う場合、Ionic 4アプリをビルドした後にそれを各プラットフォームのディレクトリにコピーする作業が必要になる。



# Ionicアプリをビルド
$ ionic build

# iOSのディレクトリにコピー
$ npx cap copy ios

# Androidのディレクトリにコピー
$ npx cap copy android

ネイティブの機能を呼び出すにはプラグインを使う

カメラ・プッシュ通知・GPS・NFCなどなど、端末の機能にアクセスするにはWebのAPIだけでは足りないので、Capacitorを通してネイティブのAPIを呼び出すことになる。ネイティブのAPIの呼び出しをJavaScriptから行えるようにするプラグインが多数公開されているので、基本的にそれを使う。

プラグインとしてまだ公開されていないような機能を使いたい場合は、自分でプラグインを書いて対応することも可能らしい。

プラグインのリストはこちら。

Cordovaのプラグインの数と比べるとまだ少ない。しかし、CapacitorではCordovaのプラグインも使えるようなので、既存のCordovaで使っていた機能を提供するようなCapacitorプラグインがまだ無くてもCordovaプラグインを使うことで対応できそう。

まとめ

Ionic 4とCapacitorを組み合わせてiOS/Androidで動作するハイブリッドアプリを作ってみた。たった数コマンドで立ち上げられてエミュレーターで確認できたので非常に開発UXが高そう。まだBeta版なので、プラグインを使って機能を開発していったら何かしら困ることが出てくるかもしれないが、ドキュメントのわかりやすさも含めて期待できるのでどんどん試していきたい。