はじめに
個人的に、Webエンジニアがモバイルアプリを作りたくなったとき、Capacitorは真っ先に検討すべき選択肢だと思っています。
GitHubで約14,500スター、Ionicチームが開発しているクロスプラットフォームのネイティブランタイム。正直なところ、「Cordovaの後継」という説明だけでは魅力が伝わりきらないんですよね。
30代になって思うのは、新しい技術を習得する時間は限られているということ。ReactやVueを使えるなら、それをそのままモバイルアプリにできる。この「既存スキルの再利用」がCapacitorの本質だと思います。
Capacitorとは
CapacitorはWeb技術(HTML/CSS/JavaScript)で作ったアプリを、iOS、Android、PWAとして動かすためのネイティブランタイムです。
特徴的なのは、WebViewでアプリを動かしつつ、ネイティブSDKに完全アクセスできる点。カメラ、ファイルシステム、プッシュ通知など、Webでは普通使えない機能が使える。そしてApp StoreやGoogle Playにも普通に出せます。
フレームワークは選びません。React、Vue、Angular、Svelte、さらには素のHTML/JS/CSSでもOK。既存のWebアプリがあるなら、そこにCapacitorを追加するだけでモバイルアプリ化できる。
特徴・メリット
1. 既存のWebアプリをそのまま使える
これ、意外とインパクトが大きいんですよ。
ReactやVueで作ったWebアプリがあるとして、それをモバイルアプリにしたい。React NativeやFlutterだと、UIを一から書き直す必要がある。でもCapacitorなら、既存のコードがほぼそのまま動く。
npm install @capacitor/core @capacitor/cli
npx cap init
npx cap add ios
npx cap add android
これだけ。時短になるし、学習コストも低い。
2. ネイティブ機能へのフルアクセス
WebViewベースだからといって機能が制限されるわけではない。
- カメラ・写真ライブラリ
- ファイルシステム
- 位置情報(GPS)
- プッシュ通知
- 生体認証(Face ID、指紋認証)
- ハプティクス(振動フィードバック)
- ネットワーク状態監視
10以上のコアプラグインが公式で提供されている。足りなければ自作もできるし、Cordovaプラグインとの互換レイヤーもある。
3. Cordovaからの移行がスムーズ
Cordovaを使っているプロジェクトからの移行先として、Capacitorは最適解だと思います。
- 既存のCordovaプラグインがほぼそのまま動く
- 設定ファイルの移行ガイドが充実
- 段階的な移行が可能
Cordovaは開発が停滞気味なので、長期的にはCapacitorに移行しておいた方が安心。
4. PWAとネイティブアプリの両立
1つのコードベースから:
- iOSネイティブアプリ
- Androidネイティブアプリ
- PWA(Progressive Web App)
この3つを同時にデプロイできる。「とりあえずPWAで出して、軌道に乗ったらストアにも出す」という戦略が取りやすい。
5. モダンな開発体験
- TypeScriptファーストの設計
- ES Modules対応
- 自動的なNPM依存関係管理
- Hot Reload対応の開発サーバー
Cordova時代の「なんか古い」という感覚がない。2024年以降の開発体験として普通に快適。
インストール方法
新規プロジェクトの場合
一番簡単なのはこれ:
npm init @capacitor/app@latest
対話形式でプロジェクト名やディレクトリを設定できます。
既存プロジェクトへの追加
ReactやVue、Angularのプロジェクトが既にある場合:
# コア依存関係をインストール
npm install @capacitor/core
npm install -D @capacitor/cli
# Capacitorを初期化
npx cap init
CLIがアプリ名とパッケージID(com.example.appみたいなやつ)を聞いてくる。設定ファイル(capacitor.config.ts)が生成されます。
プラットフォームの追加
# iOSを追加
npm install @capacitor/ios
npx cap add ios
# Androidを追加
npm install @capacitor/android
npx cap add android
これでios/とandroid/ディレクトリが作成される。XcodeやAndroid Studioで開いてビルドできます。
コードの同期
Webアプリをビルドした後、ネイティブプロジェクトに同期する:
# Webアプリをビルド
npm run build
# ネイティブプロジェクトに同期
npx cap sync
cap syncは依存関係のインストールとWebアセットのコピーを一括でやってくれる。
基本的な使い方
プロジェクト構造
my-capacitor-app/
├── src/ # Webアプリのソース
├── dist/ # ビルド成果物(ここがネイティブに同期される)
├── ios/ # Xcodeプロジェクト
├── android/ # Android Studioプロジェクト
├── capacitor.config.ts
└── package.json
設定ファイル
capacitor.config.tsで基本設定:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist',
server: {
androidScheme: 'https'
}
};
export default config;
webDirはビルド成果物のディレクトリ。ViteならだいたいdistかビルドのNuxtなら.output/publicとか。
カメラを使う
npm install @capacitor/camera
npx cap sync
import { Camera, CameraResultType } from '@capacitor/camera';
async function takePicture() {
const image = await Camera.getPhoto({
quality: 90,
allowEditing: true,
resultType: CameraResultType.Uri
});
// 撮影した写真のURIを取得
const imageUrl = image.webPath;
console.log('撮影完了:', imageUrl);
}
iOS/Androidの権限設定は自動でやってくれる。
ファイルシステム
npm install @capacitor/filesystem
npx cap sync
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
// ファイルの書き込み
async function writeFile() {
await Filesystem.writeFile({
path: 'secrets/text.txt',
data: 'これはテストデータです',
directory: Directory.Documents,
encoding: Encoding.UTF8,
});
}
// ファイルの読み込み
async function readFile() {
const contents = await Filesystem.readFile({
path: 'secrets/text.txt',
directory: Directory.Documents,
encoding: Encoding.UTF8,
});
console.log('ファイル内容:', contents.data);
}
位置情報
npm install @capacitor/geolocation
npx cap sync
import { Geolocation } from '@capacitor/geolocation';
async function getCurrentPosition() {
const coordinates = await Geolocation.getCurrentPosition();
console.log('現在地:', {
latitude: coordinates.coords.latitude,
longitude: coordinates.coords.longitude
});
}
// 位置情報の変化を監視
const watchId = await Geolocation.watchPosition({}, (position, err) => {
if (position) {
console.log('位置が更新されました:', position.coords);
}
});
プッシュ通知
npm install @capacitor/push-notifications
npx cap sync
import { PushNotifications } from '@capacitor/push-notifications';
async function initPushNotifications() {
// 権限をリクエスト
let permStatus = await PushNotifications.checkPermissions();
if (permStatus.receive === 'prompt') {
permStatus = await PushNotifications.requestPermissions();
}
if (permStatus.receive !== 'granted') {
throw new Error('プッシュ通知の権限がありません');
}
// 登録
await PushNotifications.register();
// トークン受信
PushNotifications.addListener('registration', (token) => {
console.log('プッシュトークン:', token.value);
// サーバーにトークンを送信
});
// 通知受信
PushNotifications.addListener('pushNotificationReceived', (notification) => {
console.log('通知を受信:', notification);
});
}
ネイティブアプリの起動
# iOSシミュレータで起動
npx cap run ios
# Androidエミュレータで起動
npx cap run android
# 実機で起動(デバイス接続時)
npx cap run ios --device
npx cap run android --device
実践的なユースケース
オフライン対応のメモアプリ
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { Network } from '@capacitor/network';
// ネットワーク状態を監視
Network.addListener('networkStatusChange', async (status) => {
if (status.connected) {
// オンラインに復帰したらローカルデータを同期
await syncLocalNotes();
}
});
async function saveNote(id: string, content: string) {
// ローカルに保存
await Filesystem.writeFile({
path: `notes/${id}.json`,
data: JSON.stringify({ id, content, updatedAt: new Date().toISOString() }),
directory: Directory.Documents,
encoding: Encoding.UTF8,
});
// オンラインならサーバーにも同期
const status = await Network.getStatus();
if (status.connected) {
await syncToServer(id, content);
}
}
QRコードスキャナー
npm install @capacitor-mlkit/barcode-scanning
npx cap sync
import { BarcodeScanner } from '@capacitor-mlkit/barcode-scanning';
async function scanQRCode() {
// 権限チェック
const { camera } = await BarcodeScanner.checkPermissions();
if (camera !== 'granted') {
await BarcodeScanner.requestPermissions();
}
// スキャン実行
const { barcodes } = await BarcodeScanner.scan();
if (barcodes.length > 0) {
console.log('スキャン結果:', barcodes[0].rawValue);
return barcodes[0].rawValue;
}
}
生体認証
npm install @capacitor-community/biometric-auth
npx cap sync
import { BiometricAuth, BiometryType } from '@capacitor-community/biometric-auth';
async function authenticateWithBiometrics() {
// 利用可能かチェック
const { isAvailable, biometryType } = await BiometricAuth.checkBiometry();
if (!isAvailable) {
console.log('生体認証は利用できません');
return false;
}
console.log('認証タイプ:', biometryType === BiometryType.faceId ? 'Face ID' : 'Touch ID/指紋');
try {
await BiometricAuth.authenticate({
reason: 'アプリのロックを解除します',
cancelTitle: 'キャンセル',
});
return true;
} catch (error) {
console.log('認証失敗:', error);
return false;
}
}
ビルドと配布
プロダクションビルド
# Webアプリをビルド
npm run build
# ネイティブに同期
npx cap sync
# XcodeでiOSをビルド
npx cap open ios
# Android StudioでAndroidをビルド
npx cap open android
XcodeやAndroid Studioが開くので、そこからArchive→App Store/Play Storeに申請できます。
ライブアップデート(Appflow)
ストア審査なしでWebレイヤーだけ更新したい場合、IonicのAppflowサービスと連携できます。
npm install @capacitor/live-updates
ただし、無料プランには制限があるのでプロダクションで使うなら要確認。
React Nativeとの使い分け
よく比較されるので、個人的な見解を書いておきます。
Capacitorを選ぶケース:
- 既存のWebアプリをモバイル化したい
- Web開発チームのスキルセットを活かしたい
- PWAとネイティブアプリを両方出したい
- Cordovaから移行したい
React Nativeを選ぶケース:
- ネイティブに近いパフォーマンスが必要
- ネイティブUIコンポーネントを使いたい
- Reactエコシステムにどっぷり浸かりたい
正直、一般的なアプリならCapacitorで十分。ゲームや複雑なアニメーションが必要な場合はReact Nativeの方が向いているかもしれません。
まとめ
Capacitorを導入して感じた変化:
- 開発効率: Webのスキルがそのまま使える
- コード共有: 1つのコードベースでWeb/iOS/Android全対応
- ネイティブ機能: プラグインで必要な機能は大体揃う
- 移行コスト: 既存WebアプリやCordovaからの移行が楽
- 学習コスト: 新しいフレームワークを覚える必要なし
正直なところ、「Webアプリをモバイルアプリにしたい」という要件なら、Capacitor一択ですね。
特に「既存のReact/Vueアプリがある」「チームにWeb開発者しかいない」「とりあえず動くものを早く出したい」という人には最適。FlutterやReact Nativeを習得する時間がない30代エンジニアにこそ試してほしいツールです。
まだ試していない人は、まずnpm init @capacitor/app@latestで新規プロジェクトを作って、iOSシミュレータで動かしてみてください。「Webアプリがそのままネイティブで動く」体験は、なかなか新鮮ですよ。