はじめに
「iOSアプリとAndroidアプリ、それぞれSwiftとKotlinで書き分けるのは正直しんどい」——モバイルアプリ開発に携わったことがあるなら、一度はそう感じたことがあるのではないでしょうか。プラットフォームごとに言語もUIコンポーネントも異なり、同じ機能を2度、3度と実装し直す作業は決して小さくないコストです。
Ionicは、そんな悩みをWeb技術で解決するUIツールキットです。HTML・CSS・JavaScriptという使い慣れた技術スタックだけで、iOS・Android・Webの3プラットフォームに対応したアプリを1つのコードベースから作り出せます。この記事では、Ionicの特徴からインストール方法、実際のコード例までを紹介します。
とはいえ、読むより触った方が早いと思います。ion-inputやion-toggle、ion-buttonといったIonicのコアコンポーネントは、実はReactもAngularもインストールせずブラウザだけで動きます。まずは実際に触って動きを確認してみましょう。
Ionicとは
Ionicは、ネイティブ品質のiOSアプリ・Androidアプリ・Progressive Web App(PWA)を、Web技術だけで構築できるオープンソースのUIツールキットです。React・Angular・Vueのいずれのフレームワークとも組み合わせられるほか、フレームワークに依存しないWeb Componentsとしても利用できます。ネイティブ機能へのアクセスには、姉妹プロジェクトである「Capacitor」を組み合わせるのが定番の構成です。
主な特徴
- 豊富なUIコンポーネント - タブバーやモーダル、リストなど、モバイルUIに必要な部品が一通り揃っており、iOS・Androidそれぞれのデザインガイドラインに沿った見た目に自動で切り替わります
- フレームワークを選ばない - React・Angular・Vueいずれでも同じ感覚で使え、既存プロジェクトの技術スタックを崩さずに導入できます
- Capacitorとの連携 - カメラやGPS、プッシュ通知といったネイティブ機能を、公式・コミュニティ製のプラグイン経由で呼び出せます
- PWA対応が標準 - 同じコードでWebアプリとしても公開できるため、ストア審査を待たずに機能を届けられます
インストール
Reactプロジェクトを例に、Ionicの導入手順を見てみましょう。まずはIonic CLIをグローバルにインストールします。
npm install -g @ionic/cli
新規プロジェクトを作成する場合は、以下のコマンドでReactテンプレートを選択します。
ionic start my-app tabs --type=react
cd my-app
ionic serve
既存のReactプロジェクトに追加する場合は、パッケージを個別にインストールします。
npm install @ionic/react @ionic/react-router
ネイティブアプリとしてビルドする場合は、Capacitorも合わせて導入します。
npm install @capacitor/core @capacitor/ios @capacitor/android
npx cap init
Ionicのサンプルを動かす
Ionicの各UIコンポーネントは@ionic/coreが提供するWeb Componentsとして実装されているため、ReactやAngularのプロジェクトを用意しなくても、<script type="module">で読み込むだけでブラウザ上にそのまま描画できます。下のフォームではion-input(テキスト入力)、ion-toggle(スイッチ)、ion-button(ボタン)を組み合わせています。名前欄を書き換えたり、トグルをON/OFFしてから「送信」を押すと、入力内容がそのまま下の結果欄に反映されます。
要点は次の数行です。カスタム要素として<ion-input>や<ion-toggle>を配置し、通常のDOM APIで値を読み取っているだけなのが分かるはずです。
import '@ionic/core/dist/ionic/ionic.esm.js'
const nameInput = document.getElementById('name')
const agreeToggle = document.getElementById('agree')
document.getElementById('submit').addEventListener('click', () => {
console.log(nameInput.value, agreeToggle.checked)
})
実際に動かせるサンプルが下です。@ionic/core本体のJavaScriptと、コンポーネントの見た目を整えるCSS(ionic.bundle.css)の両方を読み込んでから、Web Componentsをそのままdocument.body.innerHTMLで配置しています。
ion-inputの値は.valueプロパティで、ion-toggleのON/OFF状態は.checkedプロパティでそれぞれ取得できます。どちらも見慣れた<input>のAPIに近い感覚で扱える点がポイントです。名前欄を空にしてから送信したり、ion-inputのlabel-placementを"stacked"に変えてラベルの配置がどう変わるかを試してみてください。
基本的な使い方
Ionic Reactでは、通常のReactコンポーネントを書く感覚でUIを組み立てられます。以下は、ボタンをタップするとリストにアイテムが追加されるシンプルな画面の例です。
import React, { useState } from 'react';
import {
IonApp,
IonHeader,
IonToolbar,
IonTitle,
IonContent,
IonButton,
IonList,
IonItem,
IonLabel,
} from '@ionic/react';
const App: React.FC = () => {
const [items, setItems] = useState<string[]>([]);
const addItem = () => {
setItems((prev) => [...prev, `タスク ${prev.length + 1}`]);
};
return (
<IonApp>
<IonHeader>
<IonToolbar>
<IonTitle>タスク一覧</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<IonButton expand="block" onClick={addItem}>
タスクを追加
</IonButton>
<IonList>
{items.map((item, index) => (
<IonItem key={index}>
<IonLabel>{item}</IonLabel>
</IonItem>
))}
</IonList>
</IonContent>
</IonApp>
);
};
export default App;
IonHeaderやIonContentといったコンポーネントを組み合わせるだけで、iOSでは丸みのあるボタン、Androidではマテリアルデザイン風のボタンというように、プラットフォームごとに適切な見た目が自動で適用されます。
実践的なユースケース
スワイプで削除するリスト(ion-item-sliding)
モバイルアプリのTodoリストやメールアプリでおなじみの「項目を左にスワイプすると削除ボタンが出てくる」操作も、Ionicならion-item-slidingとion-item-optionsを組み合わせるだけで実装できます。タッチ操作でもマウスドラッグでも同じように反応します。
<ion-item-sliding>
<ion-item>牛乳を買う</ion-item>
<ion-item-options side="end">
<ion-item-option color="danger">削除</ion-item-option>
</ion-item-options>
</ion-item-sliding>
下のサンプルでは、カードを左にスワイプすると赤い「削除」ボタンが現れ、タップするとその項目がリストから消えます(マウスの場合はドラッグでスワイプできます)。
ion-item-optionsのside="end"は「スワイプした先(右から左)にボタンを出す」設定で、side="start"にすると逆方向のスワイプで別のアクション(既読化やお気に入り登録など)を割り当てられます。削除以外にも、ion-item-optionを複数並べれば「アーカイブ」「ピン留め」のような多段アクションも実装可能です。
Capacitorでネイティブ機能を呼び出す
実際のアプリでよくあるのが、Capacitor経由でネイティブのカメラ機能を呼び出すケースです。以下は、カメラで撮影した写真を画面に表示する例です。
import React, { useState } from 'react';
import { IonButton, IonImg, IonContent } from '@ionic/react';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
const PhotoCapture: React.FC = () => {
const [photoUrl, setPhotoUrl] = useState<string | undefined>();
const takePhoto = async () => {
const photo = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 80,
});
setPhotoUrl(photo.webPath);
};
return (
<IonContent className="ion-padding">
<IonButton expand="block" onClick={takePhoto}>
写真を撮影
</IonButton>
{photoUrl && <IonImg src={photoUrl} />}
</IonContent>
);
};
export default PhotoCapture;
@capacitor/cameraプラグインを使うことで、ネイティブのカメラAPIを直接叩くコードを書かずに、Web APIのような感覚で写真撮影機能を実装できます。同様の考え方で、位置情報やプッシュ通知、ファイルシステムへのアクセスなども公式プラグイン経由で扱えるため、「ネイティブ機能が必要だからネイティブ言語で書き直す」という判断を避けやすくなります。
まとめ
Ionicは、Web技術の知識をそのまま活かしながら、iOS・Android・PWAという3つの出力先を1つのコードベースでまかなえるUIツールキットです。React・Angular・Vueいずれの環境にも馴染みやすく、Capacitorと組み合わせることでネイティブ機能へのアクセスも無理なく実装できます。
「モバイルアプリも作りたいけれど、ネイティブ開発に踏み出すほどのリソースはない」という場面では、既存のフロントエンド資産を活かせるIonicが有力な選択肢になるはずです。まずはionic startコマンドでサンプルプロジェクトを作り、実機やシミュレータで動かしてみることをおすすめします。