MedTimer お薬タイマーという次の薬の時間がわかるアプリをCapacitorで作成することにしました。
シュミレーターを使って確認しながら作成しました。

そのあと、実機で確認したい場面で
「シミュレーターでは動くけれど、自分の iPhone に入れるにはどうすればいいの?」
というところでつまずきました。
React Nativeで作成したアプリはなんとか実機での確認がスムーズにできたので、今回はまた違う方法でつまずきました。
React Nativeでの方法は別の記事にしているのでご参照ください。

署名、証明書、プロビジョニングプロファイル、デベロッパモード……
用語が多く、しかもエラーメッセージが原因を正直に教えてくれません。
この記事では、Xcode に Apple ID を登録するところから、コードを直して実機で確認するまでの流れを順番に説明します。
実際につまずいた箇所と、その本当の原因も一緒に書いておきます。
この記事のゴール
- 自分の iPhone にアプリをインストールできる状態になる
- コードを直したあと、1つのコマンドで実機に反映できる状態になる
前提として必要なもの
- Mac(Xcode が動くもの)
- Xcode(App Store から無料でインストールできます)インストール方法は別記事をご参照ください
- iPhone と USB ケーブル
- Apple Developer Program への加入(年間 99 ドル)
最後の1つだけ有料です。
無料の Apple ID でも実機で動かせますが、署名の有効期限が 7 日しかなく、1 週間ごとに入れ直すことになります。
App Store に出すつもりがあるなら、最初から加入しておくほうが楽です。
まず理解しておきたいこと:アプリは二層構造になっています
ここを押さえておくと、あとの手順の意味がわかりやすくなります。
Capacitor で作ったアプリは、Web の部分とネイティブ(iOS)の部分の二層でできています。
あなたが書いたコード(React / TypeScript など)
↓ npm run build
dist/ フォルダ(HTML・CSS・JavaScript)
↓ npx cap sync ios
ios/ フォルダ(Xcode プロジェクト)
↓ Xcode でビルド
iPhone にインストールされるアプリ
つまり、コードを直しただけでは iPhone のアプリは変わりません。
上から下まで通す必要があります。これが後半の「3 段階」の話につながります。
署名とは何をしているのか
いちばん混乱しやすいのが「署名」です。ここは2つの別の話が混ざりがちなので、先に分けておきます。
1. コード署名
「このアプリは確かにこの開発者が作ったものです」と証明する仕組みです。2つの部品でできています。
- 証明書(Certificate) — 開発者の身元。Mac のキーチェーンに保存されます
- プロビジョニングプロファイル — 「この証明書で、このアプリを、この端末で動かしてよい」という許可証
大事なのは、これらを手作業で作る必要はないということです。
Xcode の「Automatically manage signing」にチェックを入れておけば、作成も更新も Xcode がやってくれます。一度設定すれば、その Mac にずっと残ります。
2. アップロード認証
App Store Connect にアプリを送るときの認証です。コード署名とは別物です。
Xcode の画面から送る場合は、ログイン済みの Apple ID がそのまま使われるので追加の設定は要りません。ターミナルから送る場合(xcrun altool など)は、別途「アプリ用パスワード」や API キーが必要になります。
過去にターミナルで Apple のアカウント認証をした記憶があると「また何かやらないといけないのか」と不安になりますが、それはこちらの話です。Xcode を使うなら不要です。
全体の流れ
やることは 4 ステップです。
- Xcode に Apple ID を追加する
- プロジェクトにチームを設定する
- iPhone のデベロッパモードを有効にする
- 実機にインストールする
1 と 2 は Mac 側、3 は iPhone 側の作業です。
ステップ 1:Xcode に Apple ID を追加する
- Xcode を開きます
- メニューの Xcode → Settings(
⌘ ,) - Accounts タブを開きます
- Add Apple Accounts → Emailか電話番号 → Developer Program に加入したアカウントでログインします
ログインすると、アカウントの下に Team が表示されます。ここまでで証明書は自動的に作られます。
チームが複数表示される場合
個人名のチームと、屋号(法人名)のチームが両方ある、ということがあります。どちらで出すかを最初に決めておいてください。
理由は、Bundle ID(com.example.myapp のような識別子)は最初に登録したチームに紐づき、あとから別のチームへ移すのが非常に面倒だからです。
どちらのチームか判断できないときは、ターミナルで確認できます。
security find-identity -v -p codesigning
証明書の一覧が出てきます。どの証明書がどの組織のものかを詳しく見るには、次のようにします。
security find-certificate -a -c "Apple Development" -p |
openssl x509 -noout -subject
結果はこのような形式です。
subject= UID=XXXXXXXXXX, CN=Apple Development: 名前 (XXXXXXXXXX),
OU=XXXXXXXXXX, O=組織名, C=US
O= が組織名、OU= が Team ID です。O= を見れば、そのチームが個人名なのか屋号なのかがわかります。
ステップ 2:プロジェクトにチームを設定する
Xcode でプロジェクトを開きます。Capacitor のプロジェクトなら、ターミナルから開くのが簡単です。
npx cap open ios
Xcode が開いたら、次の順に操作します。
- 左のツリーで App を選びます
- TARGETS の App を選びます
- Signing & Capabilities タブを開きます
- Automatically manage signing にチェックを入れます
- Team のプルダウンから、使うチームを選びます


これでプロジェクトファイルに Team ID が書き込まれ、証明書とプロビジョニングプロファイルが自動で用意されます。
ステップ 3:iPhone のデベロッパモードを有効にする
ここでいちばん時間を取られました。
ステップ 2 まで終えたところで、Xcode にこんな警告が出ました。
⚠ Communication with Apple failed
Your team has no devices from which to generate a provisioning profile.
Connect a device to use or manually add device IDs in
Certificates, Identifiers & Profiles.
⚠ No profiles for 'com.example.myapp' were found
「Communication with Apple failed(Apple との通信に失敗)」という見出しなので、ネットワークかアカウントの問題かと疑ってしまいます。契約の承諾状況を確認したり、サインインし直したり、あれこれ試しました。
しかし原因は iPhone 側にありました。
iPhone を USB で接続してもエラーが消えなかったので、ターミナルから直接ビルドを試したところ、はっきりした理由が出てきました。
xcodebuild -scheme App -configuration Debug \
-destination 'id=(端末のID)' -allowProvisioningUpdates build
error: Developer Mode disabled
To use (端末名) for development, enable Developer Mode in
Settings → Privacy & Security.
iOS 16 以降、デベロッパモードを有効にしていない端末は、Xcode から開発用の端末として扱えません。
端末が「使えない」状態なので、チームへの端末登録もできず、結果として「チームに端末が 1 台もないのでプロファイルを作れない」というエラーになっていたわけです。
有効にする手順(iPhone 側)
- 設定 → プライバシーとセキュリティ
- 一番下までスクロールして デベロッパモード
- オンにします
- 再起動を求められるので再起動します(ここは省略できません)
- 再起動後、ロックを解除するとダイアログが出るので「オンにする」→ パスコードを入力します
この項目は、一度 Xcode に接続したことのある端末にしか表示されません。見当たらない場合は、USB ケーブルを挿し直してみてください。
有効にしたあと Xcode に戻り、上部のデバイス選択から自分の iPhone を選んで Try Again を押すと、今度はプロビジョニングプロファイルが作られてエラーが消えました。
ここでの教訓
Xcode のエラーは、見出しではなく本文を読むほうが早いです。「Communication with Apple failed」は総称的な見出しで、本当の原因はその下に書かれています。
それでも原因が絞れないときは、ターミナルから xcodebuild を直接叩くと、より具体的なメッセージが出ます。GUI が飲み込んでしまう情報が見えるので、詰まったときの手段として覚えておくと便利です。
ステップ 4:実機にインストールする
ここまで来れば、あとは簡単です。
Xcode を使う場合は、上部のデバイス選択で自分の iPhone を選び、▶︎ を押します。
初回は iPhone 側で「信頼されていない開発者」と表示されることがあります。その場合は 設定 → 一般 → VPN とデバイス管理 から自分の開発者アカウントを信頼してください。
ターミナルを使う場合は次のようになります。
# 接続中の端末を確認する
xcrun devicectl list devices
# ビルドする
xcodebuild -project ios/App/App.xcodeproj -scheme App \
-configuration Debug -destination 'id=(端末のID)' \
-allowProvisioningUpdates build
# インストールする
xcrun devicectl device install app --device '(端末のID)' \
(ビルド成果物のパス)/App.app
USB ケーブルは繋いだままにする必要がありますか?
アプリを使うだけなら不要です。
一度インストールすればアプリは iPhone の中に残るので、ケーブルを抜いて普段どおり使えます。通知の動作確認などは、むしろ繋いでいない状態のほうが実際の使い方に近くなります。
ケーブルが必要なのは新しいビルドを入れ直すときだけです。
なお Developer Program に加入している場合、プロビジョニングプロファイルの有効期限は 1 年です。それまでは入れ直さなくてもアプリは起動し続けます。
無線でも入れ直せます
- Xcode → Window → Devices and Simulators
- 端末を選びます
- Connect via network にチェックを入れます
以降は同じ Wi-Fi につながっていれば、ケーブルなしでインストールできます(この設定をする最初の 1 回だけ USB 接続が必要です)。
コードを直したあと、どう反映するのか
ここが Capacitor 特有の、いちばん引っかかりやすいところです。
npm run build だけでは反映されません。 記事の前半に書いた二層構造の話が、ここで効いてきます。
npm run build # dist/ を作る(Web 側の成果物)
npx cap sync ios # dist/ を iOS プロジェクトにコピーする
# ← これを忘れると「直したのに変わらない」
(Xcode でビルド&インストール)
npm run build は Web 側のファイルを作るだけで、iOS プロジェクトの中身はまだ古いままです。npx cap sync ios でコピーして、はじめてアプリに反映されます。
「ちゃんと直したのに実機では変わっていない」というときは、まず cap sync を忘れていないか確認してください。
Capacitor が用意している npm run ios は、この 2 つに加えて Xcode を開くところまでやってくれます。あとは ▶︎ を押すだけです。
1 コマンドにまとめる
毎回 Xcode を開くのは手間なので、最後までターミナルで完結するスクリプトを用意しました。scripts/run-ios.sh として保存します。
#!/bin/bash
set -euo pipefail
cd "$(dirname "$0")/.."
echo "==> Web をビルド"
npm run build
echo "==> iOS プロジェクトへ反映"
npx cap sync ios
echo "==> 接続中の iPhone を探す"
UUID_RE='[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}'
DEVICE_ID=""
DEVICE_ID="$(
xcrun devicectl list devices 2>/dev/null |
grep -E 'connected|available' |
grep -Eo "$UUID_RE" |
head -1
)" || true
if [ -z "$DEVICE_ID" ]; then
echo "接続中の iPhone が見つかりません。" >&2
exit 1
fi
echo "==> 実機向けにビルド(${DEVICE_ID})"
DERIVED="$(mktemp -d)"
trap 'rm -rf "$DERIVED"' EXIT
xcodebuild \
-project ios/App/App.xcodeproj \
-scheme App \
-configuration Debug \
-destination "id=$DEVICE_ID" \
-derivedDataPath "$DERIVED" \
-allowProvisioningUpdates \
build >/dev/null
echo "==> iPhone にインストール"
xcrun devicectl device install app \
--device "$DEVICE_ID" \
"$DERIVED/Build/Products/Debug-iphoneos/App.app"
echo "==> 完了"
端末の ID は毎回自動で探すので、書き換える必要はありません。
package.json に登録します。
{
"scripts": {
"ios:device": "bash scripts/run-ios.sh"
}
}
これで、次のコマンドだけで実機に反映できます。
npm run ios:device
スクリプトを書くときにハマった点
このスクリプトを作る途中で、原因のわかりにくいエラーに遭遇しました。
scripts/run-ios.sh: line 43: DEVICE_ID?: unbound variable
DEVICE_ID は確かに定義しているのに「未定義」と言われます。原因はこの行でした。
echo "==> 実機向けにビルド($DEVICE_ID)"
全角の括弧「( )」のすぐ隣に変数を置いていたためです。 bash が全角の「)」まで変数名の一部として読み込み、DEVICE_ID) という存在しない変数を参照していました。
波括弧で範囲を明示すると直ります。
echo "==> 実機向けにビルド(${DEVICE_ID})"
日本語のメッセージを出すシェルスクリプトでは、変数の直後に全角文字が来ないか気をつけてください。${} で囲む習慣をつけておくと安全です。
次のステップ:TestFlight と App Store 提出
実機で動くようになったら、提出に向けた確認は TestFlight を使います。
ここで誤解しやすいのですが、TestFlight はシミュレーターの話ではありません。
App Store Connect にビルドをアップロードし、iPhone の TestFlight アプリ経由で実機にインストールする仕組みです。
Xcode で Archive
→ App Store Connect にアップロード
→ TestFlight に表示される
→ iPhone の TestFlight アプリからインストール
Archive してアップロードするところまでは、審査に出すのとまったく同じ工程です。その後「審査に出す」か「TestFlight で自分だけ試す」かが分かれるだけなので、提出前の確認としては手数がほとんど増えません。
手順は次のとおりです。
- App Store Connect で、Bundle ID に対応するアプリを作成しておきます
- Xcode 上部のデバイス選択を Any iOS Device (arm64) に変更します
- Product → Archive を実行します
- 完了後に開く Organizer で Distribute App → TestFlight & App Store → Upload
配布用の証明書(Apple Distribution)は、この過程で Xcode が自動的に作ってくれます。事前準備は要りません。
開発中は USB、最終確認は TestFlight
使い分けの目安です。
| 方法 | 用途 | 特徴 |
|---|---|---|
| USB 接続して実行 | 開発中の素早い確認 | すぐ入る。Debug ビルドなので提出物そのものではない |
| TestFlight | 提出前の最終確認 | 審査に出すのと同じ Release ビルド。アップロードに 10〜30 分 |
開発中は USB でどんどん試し、直すところを直してから、最後に TestFlight で提出物そのものを確認する、という順番が効率的です。
React Nativeで作成したアプリは、実機で確認した際にバグが何度も見つかって、その度にビルドして時間がかかりました。最初にUSBを使った確認をしておくことをかなりお勧めします。
まとめ
つまずきやすいポイントを整理します。
- 署名は自動でよい。 Xcode に Apple ID を追加し、Automatically manage signing を有効にすれば足ります。証明書を手作りする必要はありません
- コード署名とアップロード認証は別物。 ターミナルでの認証は、Xcode から送るなら不要です
- チームは最初に決める。 Bundle ID は最初に登録したチームに紐づき、あとから移すのは大変です
- iOS 16 以降はデベロッパモードが必須。 「チームに端末がない」というエラーの裏に、これが隠れていることがあります
npm run buildだけでは反映されない。npx cap sync iosを忘れないでください- エラーは見出しではなく本文を読む。 詰まったら
xcodebuildを直接叩くと具体的な原因が見えます
最初の 1 回を通すまでが山です。一度環境ができてしまえば、あとは npm run ios:device のような 1 コマンドで確認できるようになります。
この記事のカテゴリー
開発ブログ の記事をもっと見る →