【iOSアプリ】Capacitor で作った iOS アプリを「実機で」動かす方法|初心者|Xcode に Apple ID を追加

MedTimer お薬タイマーという次の薬の時間がわかるアプリをCapacitorで作成することにしました。

シュミレーターを使って確認しながら作成しました。

「MedTimer お薬タイマー」をiOSアプリにしてみる話|React NativeじゃなくてCapacitorを使う|Xcodeで表示確認
個人開発しているお薬管理アプリ「MedTimer」、ずっとWebアプリとして作ってきたのですが、そろそろiPhoneアプリ化したいと思い立ちました。理由はシンプルで、Webブラウザだと画面を閉じた瞬間に通知が届かなくなってしまうから。お薬の…

そのあと、実機で確認したい場面で

「シミュレーターでは動くけれど、自分の iPhone に入れるにはどうすればいいの?」

というところでつまずきました。

React Nativeで作成したアプリはなんとか実機での確認がスムーズにできたので、今回はまた違う方法でつまずきました。

React Nativeでの方法は別の記事にしているのでご参照ください。

【初心者】気分記録アプリ、公開への道⑦|Expo Goのあと、iOSアプリを審査に出すまでの全手順
Expo Goで実機に表示できるところまでは、なんとかたどり着きました。でもそこから先、「で、これをどうやってApp Storeに出すの?」がまったく分かりませんでした。調べても、Xcodeでビルドする記事とEASを使う記事が混ざっていて、…

署名、証明書、プロビジョニングプロファイル、デベロッパモード……
用語が多く、しかもエラーメッセージが原因を正直に教えてくれません。

この記事では、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 ステップです。

  1. Xcode に Apple ID を追加する
  2. プロジェクトにチームを設定する
  3. iPhone のデベロッパモードを有効にする
  4. 実機にインストールする

1 と 2 は Mac 側、3 は iPhone 側の作業です。

ステップ 1:Xcode に Apple ID を追加する

  1. Xcode を開きます
  2. メニューの Xcode → Settings⌘ ,
  3. Accounts タブを開きます
  4. 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 が開いたら、次の順に操作します。

  1. 左のツリーで App を選びます
  2. TARGETS の App を選びます
  3. Signing & Capabilities タブを開きます
  4. Automatically manage signing にチェックを入れます
  5. 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 側)

  1. 設定 → プライバシーとセキュリティ
  2. 一番下までスクロールして デベロッパモード
  3. オンにします
  4. 再起動を求められるので再起動します(ここは省略できません)
  5. 再起動後、ロックを解除するとダイアログが出るので「オンにする」→ パスコードを入力します

この項目は、一度 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 年です。それまでは入れ直さなくてもアプリは起動し続けます。

無線でも入れ直せます

  1. Xcode → Window → Devices and Simulators
  2. 端末を選びます
  3. 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 で自分だけ試す」かが分かれるだけなので、提出前の確認としては手数がほとんど増えません。

手順は次のとおりです。

  1. App Store Connect で、Bundle ID に対応するアプリを作成しておきます
  2. Xcode 上部のデバイス選択を Any iOS Device (arm64) に変更します
  3. Product → Archive を実行します
  4. 完了後に開く 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 コマンドで確認できるようになります。

タイトルとURLをコピーしました