フロントエンドエンジニアが初めてIonic(React), Capacitor/Cordovaでハイブリッドアプリを作って感じた、実装着手前に知っておくべき事

# はじめに
Webの開発はやっているけれども、アプリも作ってみたい。Webはマルチプラットフォーム対応するのが当たり前の感覚なので、アプリ作るならiOS/Android両方作ってみたい。けど、SwiftとKotlinを両方覚えるのは腰が重い...。

そんな事を思っている中で、今年ハイブリッドアプリ開発のプロジェクトに携わる機会がありました。
春ごろから技術調査などを行い、夏ごろからチームを組んで開発し、年内に一区切りをつけられることができました。

アプリ知識ゼロなフロントエンドエンジニアがアプリを作りきった経験から、同じようにハイブリッドアプリを作ってみたいと考えているフロントエンドエンジニアの人向けに、こういった知識や考えがあれば良いというものをまとめました。

# 利用する技術
## Ionic Framework
アプリとWebの違いは「UI」によく表れます。
例えば、アプリの下部には「メニューバー」といったものがよく配置されています。他にも、「本当にいいですか？／『キャンセル』『OK』」といったダイアログも、ゲームのような独自デザインでない限りは、OS別にデザインやレイアウトが同じだったりします。

Appleだと「Human Interface Guidelines (HIG)」、Googleだと「マテリアルデザイン」と、ガイドラインが定められており、これに準拠した作り方にすると、各OSのユーザーは違和感なく操作する事ができます。

**Ionic Framework** (以下、Ionic)はこういったアプリ独自かつOSによって異なるUIを、Webの開発フレームワーク/ライブラリ内で実現するための、**UIフレームワーク**です。

▼Ionic Framework
https://ionicframework.com/

Angular のみのサポートで長く利用されてきていましたが、ここ1,2年で **React, Vueを次々と対応** するようになり、日本でもフロントエンドエンジニアにとって扱いやすいものになっているのではないかなと思います。また、[日本のユーザーコミュニティ](https://twitter.com/ionic_japan)があり、一部ドキュメントが翻訳されています。

私が技術選定していた2020年春時点で、Reactへの対応が公式サポートされていたので、今回はReactで書く事にしました。

まず、このIonicの使い方を把握する必要があります。

## Capacitor / Cordova
前述のIonicはあくまでUIフレームワークなので、Ionicで作ったものは、雑に言うとまだ「Webアプリの状態」です。
これを「アプリ」として動かすためには、Webアプリを中で表示するための「箱」のようなアプリが必要です。

また、アプリとWebの違いで挙げられるのが、「デバイスAPI」へのアクセスです。
例を挙げると「端末の輝度」だったり「ファイルシステム」だったり、アプリらしいところでいうと「プッシュ通知」もデバイスAPIへのアクセの1つと言えると思います。
これらはスマートフォンやタブレット端末が保有する機能で、要件に応じてアプリと連携する必要があります。

こういった、Webアプリが動く箱を用意してくれたり、アプリ部分でしか制御できない部分を補ってくれるフレームワークが、**Capacitor** や **Cordova** です。
Cordovaは、ハイブリッドアプリのデバイスAPIへのアクセスを補完してくれるフレームワークとして定番ですが、ここ最近Capacitorというフレームワークも活発になってきています。

▼Capacitor
https://capacitorjs.jp/

▼Cordova
https://cordova.apache.org/

違いの説明は[Capacitorの説明ページ](https://capacitorjs.jp/docs/cordova)が詳しいのでリンクを貼るだけにしますが、
今回の開発では **Capacitorをメインで使い、CordovaはCapacitorで実現できないデバイスAPIへのアクセスが必要な場合に補完的に利用する** 方針にしました。特に困った事はなかったので、どちらを使うか迷っている方がいれば参考にしていただけると嬉しいです。

## Swift / Java
えっ、ハイブリッドなのに？という話になりますが、補足的に必要になってくる事があります。今回の開発では、**プッシュ通知のSDKを組み込んで、動作させるために少しネイティブのコードを追いかけたり、書く場面がありました**。ただ、前述のCapacitorで基本部分は自動生成してくれるので、コードさえ読めれば大丈夫かなと思います。

また、コードは書かなくても、アプリをビルドするために、Xcodeや、Android Studioは使うので、アプリのビルドの仕方などそれぞれのアプリケーションの使い方も簡単には覚えておく必要があります。

# 開発前にする事・検討する事
## 開発者アカウントの登録
iOSアプリを対応する場合は、Apple Developerの[開発者アカウント](https://help.apple.com/developer-account/?lang=ja#/dev7a05c5ce1)が必要です。(手元での実機テストのみであればAppleIDでも大丈夫だそうです)

## バンドルID(Bundle Identifer) / アプリケーションIDの決定
**アプリを一意に表すもの** として `jp.co.zzzz.yyyy.xxxx` といった文字列を扱います。いわゆるドメインの逆並びとして設定されます。iOSでは Bundle Identifer、Androidでは アプリケーションIDと呼ばれています。

**このIDはアプリを公開したら変更する事ができない**のと、これに応じてiOSだとプロビジョニングファイルの作成など、必要な作業が出てくるのと、途中で変更する場合に**ソースコード内を一括置換するだけで変更できないもの**なので出来たら初めに決定しておきたいです。

また、**このIDはユーザーが目にする可能性があるもの**であるため、受託などで開発する場合は、自社の名前とかを入れないように気をつけましょう。

テストでアプリを配布する場合は、本番アプリを区別するために、末尾に `.debug` をつけるなど、別アプリとして認識できるようにする必要があります。

## プッシュ通知
プッシュ通知のサービスで何を使うかは予め決めておいが方がよいです。また、 **プッシュ通知を開いた際に特定のアクションが求められる場合はそれぞれ処理が必要になってきます。**

Capacitorの [Push Notificationプラグイン](https://capacitorjs.com/docs/apis/push-notifications#addlistener) では `pushNotificationActionPerformed` という「プッシュ通知を押してアプリがアクティブになった時」に発火するイベントがあり、これを利用して「プッシュ通知にxxxというパラメータが含まれていたら、◯◯画面に遷移する」といった制御が可能になります。（これはハイブリッド側での処理なのでJS(TS)で書けます）

ただ、事前にこの検討が必要と書いた理由として、私が今回利用することになった [Nifty Cloud Mobile Bakcend(NCMB)](https://mbaas.nifcloud.com/)からの通知の場合に、この方法ではAndroidアプリで発火せず、めちゃくちゃハマったという経緯があります。

結果的にNCMBが用意していたSDK内のServiceをextendsしたカスタムサービスを定義して、処理を追加して対応したのですが、 **この調査はWebアプリの領域ではない** ので、とても苦労しました。

この件は、NCMBが悪いのではなく、Capacitor側がFirebaseから直でプッシュを受け取る想定になっているので、他のサービスでも起こり得ると思います。

そのため、プッシュ通知のサービスを決めたら、まずは通知がきちんとできるかどうか、イベントハンドリングがきちんとできるかどうかを調査する事をおすすめしたいと思います。

## バージョニング
アプリにはバージョニングという考え方があるのですが、リリースする前提でアプリを作っていくのであれば、きちんと理解をしておいた方が良いです。

### iOS
参照する箇所によって名前が異なるのですが、わかりやすいところで言うと、XcodeのGeneralタブで表示できるところにある `Version` と `Build` の2つの値があります。

`Version` は `1.0.0` のように3つの数字で表す事が推奨されています。後述のAndroidの `versionName` と合わせると良いです。

`Build` は、同じ `Version` でも新しいビルドと古いビルドを区別するために設定するもので、正の整数(自然数)を設定します。

アプリを配信するために、App Store Connectにアプリをアップロードする必要がありますが、 **同じ `Version` と `Build` の組み合わせのアプリはアップロードする事ができません。** また、 `Version` に関しては **アプリの審査が通ったら、たとえ `Build` 番号を増やしたとしても、同じ `Version` を指定する事ができなくなります。**

https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring

https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleversion

### Android
Androidには `versionCode` と `versionName` の2つの値があります。
内部的には `versionCode` が使用され、 `versionName` はアプリ使用者向けに表示されるものです。
`versionCode` は正の整数(自然数)で管理され、新しいバージョンのアプリを作る度に値を大きいものにしていきます。
Playストアにアップロードした時点で、その `versionCode` は使用されたものになるため、**リジェクト判定されてアプリを修正した場合は `versionCode` を大きくする必要があります。**

`versionName` は、 `<メジャー>.<マイナー>.<ポイント>` の3つの数字で表すもので、iOSの `Version` と形式を揃えておくと良いと思います。

https://developer.android.com/studio/publish/versioning?hl=ja

## テスト準備
開発者のみがテストするだけであれば、シミュレーターでの起動や、Macに端末をUSBで接続するだけで良いのですが、他の非開発者メンバーが確認する場合に、どのようにアプリを配布するのかを検討しておいた方が良いです。

私は今回は、**TestFairy** というサービスを利用しました。

https://www.testfairy.com/

ビルドしたアプリ(iOSはipaファイル、Androidはapkファイルを)をTestFairyにアップロードし、生成されるダウンロードURLを関係者に共有していました。

また、テストアプリをビルドする際に、会員情報をAPIから取得するようなアプリの場合、APIにも「開発用」「本番用」といったものが存在して、アプリ側で向き先を変更する必要があります。
また、初期リリース以降はアプリ自体にも「本番用」「開発用」が存在しますし、プッシュ通知をテストするための「プッシュテスト用」なんていうものも必要になってきます。
これら全て **同じIDのアプリとすると、端末内で共存ができない** ので、アプリの管理が煩雑になります。そのため、別のアプリとして扱う必要があります。

バンドルIDやアプリケーションIDはそれぞれ用途ごとに末尾に `.debug` `.staging` などをつけて区別する事で別アプリとして同じ端末で共存できるので、どういった状態のテストアプリが必要なのかを検討して、それぞれの状態を書き出せるように事前に計画しておきましょう。また、アプリ名も「★」とか「開）」など、 **ホーム画面で見てぱっとどの向き先のアプリかが見てわかるようにしておく** とよいです。

自分はリリース直前で知って導入できなかったのですが [bitrise](https://www.bitrise.io/) などCIをうまく利用すると、このあたりの設定やバージョニング含めてうまいことできそうです。

## ダークモードへの対応の仕方
Ionic自体は[ダークモードに対応しています。](https://ionicframework.com/jp/docs/theming/dark-mode)
また、いわゆるWebページと違い、ステータスバー部分まで描画領域が及ぶため、ダークモード時にどういった見た目とする想定かは検討しておく必要があります。

**「ダークモードに対応しない」選択をしても、ステータスバーはダークモードになったりするので、ダークモード時の見た目は確認する必要があります。**

## ノッチ端末での見え方 / セーフエリア
ここ2〜3年で、画面上部がノッチと呼ばれる凹の形をしたディスプレイの端末が増えてきました。
ブラウザで表示するWebページでは、（ランドスケープモードにしない限り）考慮する必要がありませんでしたが、アプリなので**コンテンツ領域がノッチ部分にも及びます**。その場合、ステータスバーの下にコンテンツが重なるので、普通のアプリであればノッチ部分は余白として設定しておくと良いですし、その前提でデザインをした方が良いです。

また、この部分はセーフエリアとも呼ばれているのですが、Ionicにはこの値をグローバルなcssの変数として用意してくれているので、 `padding` を設定してあげると良いです。

```css
  padding-top: var(--ion-safe-area-top);
```

## 外部サイトの表示をする時の見た目
外部サイトを表示するために、 Capacitor には [Browserというプラグイン](https://capacitorjs.jp/docs/apis/browser)があります。

ただし、このプラグインが表示するものについては、iOSでは [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) を、Androidは [Chrome Custome Tabs](https://developers.google.com/web/android/custom-tabs)を呼び出しており、これらは共に **見た目のカスタマイズがほぼできません。**

見た目のカスタマイズを行う場合、Cordovaのプラグインを使う必要がありますので、デザイナさんなどと相談した上で、どのプラグインを使うかを決める必要があります。

# さいごに
ハイブリッドアプリはアプリ部分をWebの技術スタックで作る事ができるので、フロントエンドエンジニアがアプリを開発するための導入として、とても良いと思いました。
アプリ部分だけ作ってしまえば、あとはフレームワークに任せて、Xcode / Android Studioでビルドできれば、アプリとして機能できます。

一方で、この記事で挙げた項目については、利用する技術以外は、自分が作業見積事に検討・想定できず、その後開発に入ってから困ったり、実現ができなかったり、内部スケジュールに影響を与えてしてしまったところで、Webの開発しかしていなかったら予測できない部分かと思います。

この記事が、「これからハイブリッドアプリを作ってみよう！」「2021年は新しい事してみよう！」と、ハイブリッドアプリにチャレンジするフロントエンドエンジニアの方の参考になれば幸いです。