Appiumむずいですよね。
iOSではXcode、iOSシミュレーター、WebDriverAgentが登場します。実機を使うなら、署名やプロビジョニングプロファイルも加わります。
テストしたいだけなのに、また開発環境の話をしていますね。
この記事では、iOSシミュレーターで接続を確認し、小さなアプリのボタンを1回押す。そして、表示が変わったことを確かめる。ここまでを目指します。
使うのは、macOS、Appium 3、XCUITest Driver、Pythonです。最後に、iPhone実機へ進むときの追加準備も整理します。
Android版から、何が変わるのか
前回記事で、Android版の環境構築を整理しました。
対応を並べると、こうなります。
| 役割 | Android環境 | iOS環境 |
|---|---|---|
| 操作指示を受け付ける | Appium | Appium |
| OSに合わせて操作する | UiAutomator2 Driver | XCUITest Driver |
| 開発・実行環境を用意する | Android SDK・JDK | Xcode・iOS SDK |
| 仮想端末を動かす | Android Emulator | iOS Simulator |
| 仮想端末をコマンドから扱う |
adbなど |
xcrun simctl |
| 画面の要素を調べる | Appium Inspector | Appium Inspector |
| テストを書く | Python・Appium Python Client | 同じものを使える |
Pythonの書き方には共通部分があります。一方で、端末の準備、アプリの指定方法、要素の属性は変わります。
WebDriverAgentは、何をするもの?
WebDriverAgent(WDA)は、iOS端末側で操作を仲介するテスト用プログラムです。
XCUITest DriverはWDAと通信し、WDAがAppleのXCTestの仕組みを使って画面を操作します。今回の標準的な起動方法では、必要に応じてドライバーがXcodeでWDAをビルドし、端末上で起動します。
つまり、準備する対象には「テストしたいアプリ」と「そのアプリを操作するWDA」の両方があります。
実機で「自社アプリは動くのにAppiumは動かない」となる理由の一つが、これです。自社アプリを動かす準備ができていても、WDAを動かす準備は別に必要なのです。
まず、Xcodeとシミュレーターを動かす
1.バージョンの組み合わせを決める
iOSでは、macOS・Xcode・iOS・XCUITest Driverの組み合わせを確認します。
対象のiOSに対応するXcodeとドライバーを選び、そのXcodeを使えるmacOSを確認します。公式の互換表では、XCUITest Driverの10系以降はAppium 3に対応しています。
確認先は次の2つです。
例えば、Appleの一覧ではXcode 26.3の対応macOSは15.6から26系まで、同梱iOS SDKは26.2です。「Xcodeを入れた」というだけでなく、実際の版を確認する必要があります。
本記事ではNode.js 24系、npm 10以上、Python 3.12系を前提にします。未導入の場合は、Node.jsとPythonを先に用意してください。
2.Xcode本体をインストールする
Xcodeをインストールし、一度起動します。初回のライセンス確認と必要なコンポーネントの導入を済ませます。
ここで必要なのは、Xcode本体です。xcode-select --installで導入するCommand Line Toolsだけでは、今回使うiOS SDKやシミュレーターの環境はそろいません。
ターミナルで、使用される開発環境を確認します。
xcode-select -p
xcodebuild -version
xcrun --find simctl
Xcodeを標準の場所にインストールした場合、開発環境のパスは次のようになります。
/Applications/Xcode.app/Contents/Developer
/Library/Developer/CommandLineToolsや別のXcodeが選ばれている場合は、使用するXcodeに切り替えます。次のコマンドは、このMacで既定使用する開発環境を変更します。[7]
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
Xcodeを別の場所に置いている場合は、そのパスに置き換えます。切り替え後、もう一度xcodebuild -versionで確認します。
3.iOSシミュレーターを1台起動する
Xcodeの「Settings → Components」から、使うiOSのシミュレーターランタイムをインストールします。Xcodeの版によっては「Platforms」という名称です。ランタイムは、シミュレーターの中で動かすOSです。
その後、次の順番で進めます。
- Xcodeの「Window → Devices and Simulators」を開く。
- 「Simulators」で、導入したiOSランタイムを使うiPhoneを選ぶ。なければ追加する。
- 「Xcode → Open Developer Tool → Simulator」を開く。
- Simulatorの「File → Open Simulator」から対象を起動し、ホーム画面まで進める。
ターミナルでも確認します。
xcrun simctl list devices available
起動した端末の行にある、長い英数字のUDIDを控えます。後の手順では、この値で操作対象を指定します。状態がBootedであることと、画面上で起動が完了していることも確認します。
ここまでの到達点は、Xcodeの環境でiOSシミュレーターが動いていることです。まだAppiumは登場していません。
AppiumからiOSへ接続する
4.XCUITest Driverを用意する
Appium 3が未導入なら、インストールします。導入済みの場合は、バージョン確認から進めてください。
npm install -g appium@3
appium --version
次に、iOS用のドライバーを追加します。すでに導入済みなら、一覧で版を確認し、先ほどの互換表と照合します。
appium driver install xcuitest
appium driver list --installed
appium driver doctor xcuitest
Doctorで必須条件の不足が出た場合は、その項目を解消します。任意機能向けの項目と分けて読みましょう。Doctorの成功は環境確認の一段階であり、WDAの起動成功まで保証するものではありません。
準備できたら、Appiumサーバーを起動します。
appium --address 127.0.0.1 --port 4723
このターミナルは開いたままにします。
5.Inspectorで「設定」アプリを開く
Appium Inspectorのデスクトップ版を使います。
接続先は次のとおりです。
| 設定 | 値 |
|---|---|
| Remote Host | 127.0.0.1 |
| Remote Port | 4723 |
| Remote Path | / |
| SSL | 無効 |
CapabilitiesのJSON入力欄に、次を指定します。UDIDは、先ほど控えた実際の値に置き換えてください。
{
"platformName": "iOS",
"appium:automationName": "XCUITest",
"appium:udid": "ここをシミュレーターのUDIDに置き換える",
"appium:bundleId": "com.apple.Preferences",
"appium:noReset": true,
"appium:forceAppLaunch": true,
"appium:showXcodeLog": true
}
bundleIdは、iOSアプリの識別子です。ここでは標準の「設定」アプリを指定しています。端末上にあるアプリを指定するため、インストール用のファイルは不要です。
noResetはアプリのデータ初期化を抑える設定、forceAppLaunchは起動済みでもアプリを再起動する設定です。再起動とデータ初期化は、別の操作です。
「Start Session」を押すと、WDAの準備と接続が始まります。初回はビルドが必要になるため、画面がすぐ出なくても、Appiumのログで進行状況を確認します。
設定アプリの画面と要素のツリーが取得できれば成功です。
今回の設定アプリを使うシミュレーター接続では、実機用の署名・プロビジョニング設定は不要です。
Pythonから、同じ接続を再現する
6.画面と画面構造を保存する
Inspectorのセッションを終了します。Appiumサーバーとシミュレーターは起動したままにしておきます。
別のターミナルで作業用フォルダーを作り、Pythonの仮想環境を用意します。
mkdir appium-ios-first-step
cd appium-ios-first-step
python3 -m venv .venv
source .venv/bin/activate
python -m pip install Appium-Python-Client
同じターミナルで、対象端末のUDIDを環境変数に入れます。
export APPIUM_IOS_UDID="ここをシミュレーターのUDIDに置き換える"
次の内容をcheck_ios_connection.pyとして保存します。
import os
from pathlib import Path
from appium import webdriver
from appium.options.ios import XCUITestOptions
from appium.webdriver.applicationstate import ApplicationState
from selenium.webdriver.support.ui import WebDriverWait
bundle_id = os.environ.get("APPIUM_IOS_BUNDLE_ID", "com.apple.Preferences")
options = XCUITestOptions().load_capabilities({
"platformName": "iOS",
"appium:automationName": "XCUITest",
"appium:udid": os.environ["APPIUM_IOS_UDID"],
"appium:bundleId": bundle_id,
"appium:noReset": True,
"appium:forceAppLaunch": True,
"appium:showXcodeLog": True,
})
driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
try:
WebDriverWait(driver, 15).until(
lambda d: d.query_app_state(bundle_id)
== ApplicationState.RUNNING_IN_FOREGROUND
)
if not driver.save_screenshot("ios-screen.png"):
raise RuntimeError("スクリーンショットを保存できませんでした")
Path("ios-screen.xml").write_text(driver.page_source, encoding="utf-8")
print("iOSへの接続確認に成功しました")
finally:
driver.quit()
Android版との主な違いは、XCUITestOptionsを使うこと、アプリをbundleIdで指定することです。前面で実行されているかは、query_app_stateで確認しています。
実行します。
python check_ios_connection.py
成功メッセージと、ios-screen.png、ios-screen.xmlを確認します。
ここで証明できたのは、指定したiOSアプリを起動し、画面情報を取得できることです。次に、実際の操作と結果確認を加えます。
自分のアプリで、ボタンを1回押す
7.操作結果が分かる小さなアプリを作る
ログインを最初の題材にすると、通信、アカウント、認証状態まで関係します。今回は「押すと表示が変わる」だけのアプリを作ります。
Xcodeで「File → New → Project」からiOSのAppを作成し、次を指定します。
| 項目 | 値 |
|---|---|
| Product Name | AppiumDemo |
| Interface | SwiftUI |
| Language | Swift |
| Storage | None、または永続化を使わない設定 |
ContentView.swiftを次の内容にします。
import SwiftUI
struct ContentView: View {
@State private var didTap = false
var body: some View {
VStack(spacing: 24) {
Text(didTap ? "操作できました" : "未実行")
.accessibilityIdentifier("demo.status")
Button("確認する") {
didTap = true
}
.accessibilityIdentifier("demo.confirm")
}
.padding()
}
}
実行先に、Appiumで使っているシミュレーターを選び、XcodeからRunします。「確認する」を手で押し、表示が変わることを確かめます。
このとき付けたaccessibilityIdentifierは、テストから画面要素を識別するための値です。画面に表示する文言とは別に指定できます。Appiumでは、Accessibility IDを使って要素を探せます。
次に、Xcodeのアプリターゲットの「Signing & Capabilities」などで、実際のBundle Identifierを確認します。次の値は例なので、自分のプロジェクトの値に置き換えてください。
export APPIUM_IOS_BUNDLE_ID="com.example.AppiumDemo"
Xcodeで実行中ならStopでデバッグ実行を終え、シミュレーターは起動したままにします。python check_ios_connection.pyを再実行すると、今度は自分のアプリの画面を取得できます。
なお、環境変数はターミナルごとに設定します。新しいターミナルでは、仮想環境の有効化とUDID・Bundle IDの設定をやり直してください。
8.「押せた」から「期待どおりになった」へ
先ほどのPythonファイルの先頭に、次のインポートを追加します。
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support import expected_conditions as EC
そして、tryの中で、前面表示を待つ処理の直後、スクリーンショットを保存する前に、次を追加します。
wait = WebDriverWait(driver, 10)
status = (AppiumBy.ACCESSIBILITY_ID, "demo.status")
wait.until(
lambda d: d.find_element(*status).get_attribute("label") == "未実行"
)
wait.until(
EC.element_to_be_clickable(
(AppiumBy.ACCESSIBILITY_ID, "demo.confirm")
)
).click()
wait.until(
lambda d: d.find_element(*status).get_attribute("label")
== "操作できました"
)
print("ボタン操作と表示変更の確認に成功しました")
この追加後のスクリプトは、AppiumDemo用です。環境変数が設定アプリを指している場合は動きません。
ここでは、ボタンを押す前の状態と、押した後の表示を確認しています。時間内に期待結果にならなければ、タイムアウトで失敗します。固定秒数の待機に頼らず、確認したい状態を待つ書き方です。
同じスクリプトをもう一度実行して、再び成功することも確認します。このサンプルでは、セッション開始時の再起動で@Stateが初期値に戻ります。業務アプリのDBやログイン情報まで、同じように消えるわけではありません。
要素を見つける、操作できる状態を待つ、操作後の結果を確認する。 この3つがそろって、画面操作を伴うテストになります。
アプリを渡してもらう場合は、ビルド先に注意
今回の例は、Xcodeからシミュレーターへインストール済みのアプリを使いました。
開発チームからファイルを受け取る場合は、iOSシミュレーター向けにビルドした.appを依頼します。実機向けの.ipaを、そのままシミュレーターへ入れることはできません。同じARM系でも、ビルド対象のプラットフォームが異なります。
アプリをAppiumからインストールする場合は、appium:appにファイルの絶対パスを指定します。パスは、Appiumサーバーが動くMac上のものです。
{
"platformName": "iOS",
"appium:automationName": "XCUITest",
"appium:udid": "シミュレーターのUDID",
"appium:app": "/Users/your-name/build/AppiumDemo.app"
}
実機のiPhoneへ進むと、何が増えるのか
シミュレーターで動いたら、実機へ進めます。ただし、UDIDの差し替えに加えて、端末の開発設定と署名の準備が必要です。
端末を開発用に接続する
最初はMacとiPhoneをUSBで接続し、次を確認します。
- iPhoneで「このコンピュータを信頼」を選ぶ。
- Xcodeの「Devices and Simulators」に端末が表示されることを確認する。
- iOS 16以降では「設定 → プライバシーとセキュリティ → デベロッパモード」を有効にし、画面の案内に従って再起動・確認を行う。
- 「設定 → デベロッパ → UIオートメーションを有効にする」を有効にする。
- 対象アプリを、その実機で起動できる状態にする。
実機のUDIDはXcodeで確認します。simctlはシミュレーター向けなので、実機の識別子を探す手順とは分けます。
WDAにも署名が必要
実機では、WDAをインストールして実行するために、有効な署名とプロビジョニングプロファイルが必要です。テスト対象アプリの署名とは別に準備します。
用語を分けると、次の関係です。
| 項目 | 確認すること |
|---|---|
| Team | どの開発チームでWDAを署名するか |
| 署名証明書・秘密鍵 | そのMacで開発用の署名ができるか |
| WDAのBundle Identifier | そのチームで使用できる識別子か |
| プロビジョニングプロファイル | WDAの識別子・証明書・対象端末が許可されているか |
署名で失敗したときは、WDAのプロジェクトをXcodeで開くと、問題を画面上で確認できます。
appium driver run xcuitest open-wda
このコマンドは、インストール済みドライバーに付属するWDAのプロジェクトを開きます。
XcodeでWebDriverAgentRunnerターゲットの「Signing & Capabilities」を確認します。自動署名を使う場合は、Xcodeに開発用アカウントを登録し、「Automatically manage signing」を有効にして、使用するTeamと、そのチームで使えるBundle Identifierを設定します。対象iPhoneを実行先に選び、WebDriverAgentRunnerスキームで「Product → Test」を実行して、ビルド・インストール・起動できるかを確認します。具体的な設定は、アカウントの種別とチームの署名方式に合わせます。
WDAプロジェクトへの手動設定は、ドライバーやWDAの更新時に再設定が必要になるため、ここでは原因調査の手段として使います。起動確認後はXcodeのテスト実行を停止し、継続利用する署名設定をAppium側にも指定します。
Appiumから自動署名を指定する場合は、主に次のCapabilitiesを使います。
| 設定 | 指定する内容 |
|---|---|
appium:udid |
実機のUDID |
appium:xcodeOrgId |
署名するチームのTeam ID |
appium:xcodeSigningId |
Macで利用可能な開発用署名ID |
appium:updatedWDABundleId |
WDAに使用するBundle Identifier |
WDAのテストランナーには.xctrunnerが付くため、プロファイルがその識別子も許可する構成になっているか確認します。Team IDを指定するだけで、証明書や端末登録の準備まで完了するわけではありません。
自分のアプリ、WDAの順に実機で起動できたら、実機のUDID・署名設定を指定してInspectorからセッションを開始します。Python側のCapabilitiesも同じ実機向け設定に更新し、操作・結果確認を試します。
動かないときは、どの段階で止まったかを見る
エラーが出ると、検索して見つけたCapabilitiesを次々追加したくなります。ですが、原因が分からないまま設定を増やすと、元に戻すのも難しくなります。
まずは、最後に成功したところを確認します。
| 止まったところ | 最初に確認すること |
|---|---|
xcodebuildやsimctlが使えない |
Xcode本体の導入・初回設定・選択中の開発環境 |
| シミュレーターが見つからない | iOSランタイムの導入と、作成済み端末の一覧 |
| Inspectorがサーバーにつながらない | Appiumの起動状態、4723番ポート、接続パス |
| WDAのビルド・起動に失敗する | Xcode・iOS・ドライバーの互換性とXcodeの詳細ログ |
| 実機だけWDAが動かない | 信頼、Developer Mode、UI Automation、署名・プロファイル |
| アプリをインストールできない | シミュレーター用/実機用のビルドの違い、パス、署名 |
| 画面は出るが要素が見つからない | Inspectorで見える属性、現在の画面、ダイアログ、待機条件 |
| 2回目だけ失敗する | 前回のデータ、ログイン状態、権限許可、開始画面 |
xcodebuildの終了コード65だけで「署名エラー」と決めつけないことも大切です。コード65は失敗の結果であって、原因そのものではありません。今回設定したshowXcodeLogで詳細を出し、ビルド・署名・起動のどこで失敗したかを読みます。
また、権限ダイアログが邪魔だからと、すべて自動承認する設定を入れると、「許可しない場合」のテスト条件も変わります。最初は画面に出た内容を確認し、そのケースで選ぶ操作を決めます。
最初の1本が動いたら、環境も記録する
成功したら、ターミナルの出力を作業メモに残します。
sw_vers
xcodebuild -version
xcrun simctl list runtimes
node --version
npm --version
appium --version
appium driver list --installed
python --version
python -m pip freeze > requirements.txt
端末の種類、iOSの版、テスト対象アプリの版、使用したCapabilitiesも記録します。実機では、署名に使用したTeamとWDAの識別子、プロファイルの有効期限も管理対象になります。
テストコードが変わっていなくても、XcodeやiOSの更新によって動作条件は変わります。成功した環境の組み合わせを残すことも、テストを繰り返し動かすための準備です。
さいごに
iOS版も、ボタンを押すまでにずいぶん準備しました。
それでも、「Xcodeで動く」「WDAでつながる」「要素が取れる」「操作後の結果を確かめられる」と区切れば、調べる場所を絞れます。
まずは1台、1画面、1操作。その1本を、同じ開始状態からもう一度動かせるところまで進めましょう。
本記事は2026年9月23日時点で確認した公式資料に基づく構成例です。
参考資料
- XCUITest Driver:System Requirements
- XCUITest Driver:Installation
- XCUITest Driver:Device Preparation
- Apple:Xcodeの対応OS・SDK一覧
- Appium 3:System Requirements
- Appium Python Client
- Apple:Building from the Command Line with Xcode FAQ
- Apple:Downloading and installing additional Xcode components
- Appium Inspector:Configuring and Starting a Session
- XCUITest Driver:Capabilities
- XCUITest Driver:Capability Set Examples
- Appium Python Client:アプリ操作・状態取得
- Apple:accessibilityIdentifier(_:)
- XCUITest Driver:Locator Strategies
- XCUITest Driver:Element Attributes
- Selenium:Waiting Strategies
- Apple:Running your app on simulated or physical devices
- XCUITest Driver:Basic Automatic Configuration
- XCUITest Driver:Scripts
- XCUITest Driver:Full Manual Configuration