capacitor-uiscene-migration について
Xcode 27 の要件に対応するため、Capacitor iOS プロジェクトを AppDelegate のライフサイクルから UIScene へ移行するスキルです。移行状態、独自のライフサイクル処理や URL 処理、ビルドと実行時の検証を扱います。
主な機能
- 01UIScene の移行状態を判定
- 02ライフサイクルと URL 処理を監査
- 03AppDelegate と SceneDelegate の変更を案内
- 04プラグインと SDK のライフサイクル確認
- 05ビルドとアプリ動作の検証
活用シーン
- 01Capacitor 8.4 以前を Xcode 27 に備える
- 02cap migrate の部分移行結果への対処
- 03シーン導入後のディープリンク維持
- 04プラグインの UIScene 互換性確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill capacitor-uiscene-migrationインストール後、Claude Code で/capacitor-uiscene-migrationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.jsonreferences/audit-patterns.mdpbxproj-registration.mdplugin-audit.mdscene-delegate-template.md詳細
Capacitor UIScene への移行(8.4 -> 8.5)
Capacitor 8.5 では、Xcode 27 で必要となるため、iOS テンプレートが UIScene ライフサイクルに移行しました。これは iOS のみを対象とした、破壊的変更を含むマイナーリリースです。8.5 のコアは引き続き AppDelegate の経路で動作するため、依存関係を更新しただけでアプリが壊れることはありません。Xcode 27 でビルドするには(また、Capacitor 9 にアップグレードする前に)、アプリのプロジェクトでシーンを採用する必要があります。
正式な情報源:https://capacitorjs.com/docs/updating/8-5
使用する状況
次の場合に使用します:
- アプリが
@capacitor/ios8.0~8.4 を使用している場合、または 8.5 を使用しているものの、Info.plistにUIApplicationSceneManifestがない場合 - Xcode 27 のビルド時または実行時に、「CLIENT OF UIKIT REQUIRES UPDATE: This process does not adopt UIScene lifecycle」という警告が表示される場合
npx cap migrateの実行時にUIScene migration: project is in a partial state ... Skipping automated migrationと表示された場合- シーンマニフェストを追加した後、カスタム URL スキーム、ユニバーサルリンク、
appUrlOpen、または AppDelegate のapplicationDidBecomeActiveのコードが実行されなくなった場合 - 8.5 への更新後、
tmpWindowまたはTmpViewControllerでビルドエラーが発生した場合 - プラグインの作成者から、iOS コードがシーンライフサイクルでも動作するか質問された場合
- 8.4 以前から Capacitor 8 -> 9 へのアップグレードを準備している場合
使用しない状況:
- Capacitor 8 -> 9 のパッケージ/ネイティブのアップグレード自体 ->
capacitor-app-upgrade-v8-to-v9/capacitor-plugin-upgrade-v8-to-v9 - ディープリンクを一から設定する場合 ->
capacitor-deep-linking - Android(UIScene は iOS のみです)
- ライフサイクルへの対応を超える、マルチウィンドウやサイズ変更可能なレイアウトの作業
稼働中のプロジェクトのスナップショット
!node -e "const fs=require('fs');if(!fs.existsSync('package.json'))process.exit(0);const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));const out=[];for(const s of ['dependencies','devDependencies','peerDependencies']){for(const [n,v] of Object.entries(pkg[s]||{})){if(n.startsWith('@capacitor/'))out.push(s+'.'+n+'='+v)}}const p='ios/App/App/';out.push('SceneDelegate.swift='+fs.existsSync(p+'SceneDelegate.swift'));if(fs.existsSync(p+'Info.plist'))out.push('UIApplicationSceneManifest='+fs.readFileSync(p+'Info.plist','utf8').includes('UIApplicationSceneManifest'));if(fs.existsSync(p+'AppDelegate.swift'))out.push('configurationForConnecting='+fs.readFileSync(p+'AppDelegate.swift','utf8').includes('UISceneConfiguration(name:'));console.log(out.join('\n'))"
監査の前提となる事実
UIApplicationSceneManifestが存在すると、iOS は次の AppDelegate メソッドを呼び出さなくなります:application(_:open:options:)、application(_:continue:restorationHandler:)、applicationDidBecomeActive、applicationWillResignActive、applicationDidEnterBackground、applicationWillEnterForeground。これらのメソッド内のコードは、警告もなく実行されなくなります。- AppDelegate で引き続き呼び出されるのは、
didFinishLaunchingWithOptions、applicationWillTerminate、プッシュトークンの登録(didRegisterForRemoteNotificationsWithDeviceToken/didFailToRegister...)、リモート通知の配信です。UIApplication.*Notificationのライフサイクル通知は引き続き発生します。 SceneDelegateProxy(CAPSceneDelegateProxy.swift)は、.capacitorOpenURL、.capacitorOpenUniversalLink(同じペイロード)、およびCDVPluginHandleOpenURLを再通知し、URL をApplicationDelegateProxy.shared.lastURLにも反映します。コールドスタート時の URL とユーザーアクティビティは、最初の.capacitorViewDidAppearまで保持されるため、プラグインは登録後にそれらを受け取り、App.getLaunchUrl()も動作します。- 8.5 でのみ追加された通知:
.capacitorSceneWillConnect、.capacitorSceneOpenURL、.capacitorSceneOpenUniversalLink(object =UIScene、ペイロードはuserInfo内)。8.4 では通知されません。 - JS の
pause/resumeは、ブリッジのシーンに絞り込んだUIScene.didEnterBackgroundNotification/willEnterForegroundNotificationによって発火します。 - 8.5 で削除されたもの:
TmpViewController、CapacitorBridge.tmpWindow、tmpViewControllerAppeared。これらを参照するとビルドエラーになります。bridge.viewControllerから表示してください。 - 8.5 の SceneDelegate は、コード内でウィンドウと
CAPBridgeViewController()を作成します。Main.storyboardで設定したカスタムブリッジサブクラスは、代わりに SceneDelegate でインスタンス化する必要があります。 cap migrate(CLI 8.5 以降)は、次の 3 つの要素でプロジェクトを分類します:Info.plist内のマニフェスト、ディスク上のSceneDelegate.swift、AppDelegate.swift内のUISceneConfiguration(name:。0/3 の場合は移行し、3/3 の場合は完了済みとしてスキップし、1~2/3 の場合は「部分的な状態」と警告して何もしません。既存のSceneDelegate.swiftを上書きすることはありません。UIApplication.shared.applicationState、tmpWindow、TmpViewController、カスタムのopen url:/continue userActivity:本体については警告しますが、修正はしません。
手順
監査し、報告し、確認してから編集します。ユーザーに調査結果を提示する前に、編集してはいけません。
- リポジトリの種類を判定します。
ios/App/App.xcodeprojがあればアプリです。Package.swift/*.podspecがあり、CAPPluginのソースを含み、アプリプロジェクトがなければプラグインです。手順 9 に進みます。 - バージョンとパッケージマネージャーを確認します。
@capacitor/iosは^8.5.0以上である必要があります。ios/App/Podfileがあれば CocoaPods、ios/App/CapApp-SPMがあれば SPM です。SceneDelegate はどちらでも同一です。 - スナップショットの 3 つのシグナル(対象 / 移行済み / 一部移行済み)で分類します。
references/audit-patterns.mdのコマンドを使用して監査し、ios/Appとnode_modules内のインストール済みプラグインを確認します。見つかった項目ごとにファイル名と行番号を記録します。- 次のように分類して報告します。ビルドを妨げる項目(
tmpWindow、TmpViewController)、判断が必要な項目(カスタム URL / アクティビティ / ライフサイクルの処理、既存の SceneDelegate、ストーリーボードのカスタムクラス、サードパーティ SDK への転送)、参考情報(引き続き機能するオブザーバー、applicationState、node_modules内の問題)。 - 実際に判断が必要な箇所では確認します。
open url:/continue userActivity:にカスタム処理がある場合:プロキシ呼び出しの横にあるscene(_:openURLContexts:)/scene(_:continue:)に移すか確認します(先に処理内容を提示します)。- カスタムのライフサイクルコード:
sceneDidBecomeActive/sceneWillResignActive/sceneDidEnterBackground/sceneWillEnterForeground、またはUIApplicationの通知オブザーバーに移すか確認します。 - 役目を終えた AppDelegate の URL メソッドを削除するか、そのまま残すか確認します(残しても問題ありません)。
- URL 転送を必要とする SDK(Facebook、Google Sign-In、OAuth リダイレクトライブラリ):ドキュメントで SceneDelegate のエントリーポイントを確認し、SceneDelegate から呼び出します。
- 適用します。
- 対象でテンプレートに沿った構成の場合:
npm i @capacitor/core@^8.5.0 @capacitor/ios@^8.5.0、npm i -D @capacitor/cli@^8.5.0の順に実行し、その後npx cap migrateを実行します。ログの全行を確認し、スキップされた項目はその手順に限って手動で処理します。その後、手順 6 の判断内容を適用します。 - 一部移行済み、またはカスタマイズ済みの場合:
references/scene-delegate-template.mdを使用して、不足している部分だけを統合します。既存のキー、メソッド、カスタムロジックは維持します。references/pbxproj-registration.mdに従って新しいファイルを登録します。
- 対象でテンプレートに沿った構成の場合:
- 同期してビルドします。
npx cap sync iosを実行し、その後ビルドします(「検証」を参照)。3 つのシグナルを再確認し、すべてが true であることを確認します。 - プラグインリポジトリの場合:
references/plugin-audit.mdを使用して監査と助言のみを行います。プラグインリポジトリからアプリのファイルを変更してはいけません。ユーザーから依頼された場合に限り、プラグインのソースを編集します。
検証
npx cap sync ios
xcodebuild -project ios/App/App.xcodeproj -scheme App -configuration Debug \
-destination 'generic/platform=iOS Simulator' build # use -workspace App.xcworkspace for CocoaPods
grep -rn --include=*.swift -E '\btmpWindow\b|\bTmpViewController\b' ios/App # expect nothing
デバイスまたはシミュレーターで確認します。
- アプリが Web ビューを表示して起動する(黒い画面にならない)。
- バックグラウンドからフォアグラウンドに戻したとき、JS の
pauseとresumeがそれぞれ 1 回ずつ発生する。 - カスタムスキームでの起動時(アプリ実行中):
appUrlOpenが発生する。xcrun simctl openurl booted myscheme://testを使用します。 - カスタムスキームでの起動時(アプリ終了後にリンクを開く):
appUrlOpenが発生し、App.getLaunchUrl()が URL を返す。 - アプリに関連付けられたドメインがある場合、ユニバーサルリンクがアプリにルーティングされる(実際のドメインと AASA ファイルが必要)。
- プッシュ通知:トークンが引き続き届き(
registrationイベント)、タップした通知からアプリが開く。 - 移動したカスタムロジック(フォアグラウンド復帰時の分析、SDK の URL ハンドラー)が実行される。
エラー処理
cap migrateが「partial state」と表示する -> 手作業で編集されたプロジェクトでは想定どおりです。手順 7 に従って手動でマージしてください。確認せずにファイルをリセットしないでください。- 移行後に画面が真っ黒になる -> SceneDelegate がウィンドウを作成していないか、
Info.plistのUISceneDelegateClassNameがクラスと一致していません。$(PRODUCT_MODULE_NAME).SceneDelegateとテンプレートのウィンドウ設定を使用してください。 - AppDelegate で
Cannot find 'SceneDelegate' in scopeが表示される -> ファイルが App ターゲットに含まれていません。references/pbxproj-registration.mdを参照してください。 Value of type 'CapacitorBridge' has no member 'tmpWindow'が表示される -> その参照を削除し、bridge?.viewControllerから表示してください。- ディープリンクがアプリ起動中は機能するが、起動時には機能しない -> URL の処理がまだ AppDelegate にあるか、ブリッジが表示される前にアプリが URL を読み取っています。
appUrlOpenとgetLaunchUrl()を使用してください。 - カスタムブリッジのサブクラスが使用されない(
capacitorDidLoadで登録されたプラグインが見つからない) ->scene(_:willConnectTo:)でサブクラスをインスタンス化してください。 pbxprojを手動編集した結果、Xcode でプロジェクトを開けなくなる -> 変更を元に戻し、Xcode からファイルを追加してください。
参考資料
トピックが該当する場合にのみ、参考資料を読み込んでください。
| ファイル | 読み込むタイミング |
|---|---|
references/audit-patterns.md | 手順 4:grep コマンドと各ヒットの分類方法 |
references/scene-delegate-template.md | SceneDelegate の作成またはマージ、Info.plist のマニフェスト、AppDelegate のフック、カスタムサブクラス、ライフサイクルの各パターン |
references/pbxproj-registration.md | Xcode を使わずに SceneDelegate.swift を登録する場合、または CLI で登録されたことを確認する場合 |
references/plugin-audit.md | リポジトリがプラグインの場合、またはインストール済みプラグインでライフサイクルに関するヒットが見つかった場合 |
関連スキル:capacitor-app-upgrade-v8-to-v9、capacitor-plugin-upgrade-v8-to-v9、capacitor-app-upgrades、capacitor-deep-linking、capacitor-push-notifications。