📱

SceneDelegate対応した手順や参考にした情報

に公開

概要

WWDC25のセッション動画 Make your UIKit app more flexible にて次のメジャーアップデート(つまりiOS27)にてSceneDelegate対応が必須になるため、先行して対応しました。
「そもそも自分のアプリが対象なのかよくわからない」
という人も多いのではと思ったので自分が対応した際に参考になった記事などを参考に、情報を整理しました。

SceneDelegate対応の対象か

とりあえず

  • サポートバージョンが13.0以降
  • UIKitがベース
  • AppDelegateのみでSceneDelegateはない

のであれば対象です。
SwiftUIがベースの場合は

  • SwiftUIはデフォルトでscene-basedライフサイクルを採用している
  • WindowGroupが自動的にUIWindowSceneに対応しているため対応不要
  • ただしiPadOSの複数ウィンドウ対応ではUIApplicationSceneManifestの追加が必要

という感じです。
https://developer.apple.com/documentation/swiftui/windowgroup#:~:text=standard window management.-,Important,-To enable an

app-basedやscene-basedに関しては以下の記事が大変わかりやすいかと思います。
https://techblog.lycorp.co.jp/ja/20250619a#:~:text=scene-basedライフサイクルとは?

まとめると、

  • UIKitベースかつAppDelegateしかない場合 → 移行必須
  • SwiftUIベース → 基本不要(ただしiPadOSの複数ウィンドウ対応では必要)

対応方法について

まずものすごく簡単に移行手順を話すと

  • info.plistにUIApplicationSceneManifestを追加
  • SceneDelegateを作成
  • AppDelegateにあるWindow周りを全て移行

こんな感じです。

対応する前にインプットした方がいい情報

実装イメージがわかないという人向けに最低限見ておいた方がよさそうなものを列挙しておきます。

この動画を見るとざっくりとAppDelegateからSceneDelegateへの移行がイメージ付きます。

  • WWDC19: Architecting Your App for Multiple Windows

https://developer.apple.com/videos/play/wwdc2019/258/

UIKitでscene-basedに移行するテクニカルノートを読むと移行判断や手順の概要を掴むことができます。

  • TN3187: Migrating to the UIKit scene-based life cycle

https://developer.apple.com/documentation/technotes/tn3187-migrating-to-the-uikit-scene-based-life-cycle

一旦この二つ見るだけで大体の移行に関するイメージは掴めるかと思います。

実装手順

ざっくりとした手順は上記に列挙しましたが、ここではちょっとした解説をしていきたいと思います。

info.plistにUIApplicationSceneManifestを追加

TN3187にある通り設定すれば基本問題ないです。

<key>UIApplicationSceneManifest</key>
<dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <false/> 
    <key>UISceneConfigurations</key>
    <dict>
        <key>UIWindowSceneSessionRoleApplication</key>
        <array>
            <dict>
                <key>UISceneConfigurationName</key>
                <string>Default Configuration</string>
                <key>UISceneDelegateClassName</key>
                <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                <key>UISceneStoryboardFile</key>
                <string>Main</string> 
            </dict>
        </array>
    </dict>
</dict>

重要な部分だけ少し解説を書いておきます。

  • UIApplicationSupportsMultipleScenes: マルチウィンドウのサポートを有効にする場合はtrueにしてください。
  • UISceneConfigurationName: シーン構成の一意の名前。複数でなければ基本一つでDefault Configurationで問題ありません。
  • UISceneDelegateClassName: SceneDelegateクラスの指定。基本的にはSceneDelegateと命名するのでサンプル通り$(PRODUCT_MODULE_NAME).SceneDelegateで問題ないかと思います。
    (別名つけている場合は適宜修正してください)

SceneDelegateを作成

SceneDelegateのサンプルコードも記載されています。

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    
    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        // Confirm the scene is a window scene in iOS or iPadOS.
        guard let windowScene = scene as? UIWindowScene else { return }
                
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = YourRootViewController()
        window?.makeKeyAndVisible()
    }
}

まずはこのWindowをAppDelegateから移行します。
よってAppDelegateにあるWindowは削除して、Window周りもこちらに移行していけば良いです。

AppDelegateからの移行

移行に関しては
Migrate app life-cycle logicセクションを参考にマッピングされたAPIに移行していけばいいですが、UniversalLinksなどの実装があると他に対応する必要があるのでそれは各プロダクトのAppDelegateにあるSceneに関連するものを適時移行します。

対応するマッピングとして以下のサイトが非常にわかりやすかったので貼っておきます。
https://swiftrivals.com/blog/migrating-appdelegate-to-scenedelegate

またUniversal LinksやPush通知などconnectionOptionsを扱う処理はSceneDelegateに移す必要がありますのでこの辺り参考になると思います。
https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app?utm_source=chatgpt.com

https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app?utm_source=chatgpt.com

WIP: 参考記事いくつか

移行のTips

移行作業を簡単にする

個人的に以下の方法で移行してサクッとできたので紹介します。

  1. まずAppDelegateにあるDelegate Methodを検索する
  2. 移行対象ならドキュメントにiOS 26 でDeprecatedとなる旨とSceneDelegateで使用するAPIが記載されているので対応する
    以上です。

https://developer.apple.com/documentation/UIKit/UIApplicationDelegate/application(_%3Aopen%3Aoptions%3A)?utm_source=chatgpt.com

Universal Links経由でコールド起動をするとアプリがクラッシュする

scene(_:openURLContexts:)で処理しているので問題ないかと思いましたが、Universal Links経由でコールド起動をするとアプリがクラッシュしました。
AppDelegateでは暗黙的に初期化後に処理されていたみたいですが、SceneDelegateでは明示的に扱う必要があるみたいです。つまりSceneDelegateではコールド起動時のDeepLinkや通知のハンドリング場所に注意が必要のようです。
以下の記事が大変参考になりましたが、おそらくscene(_:willConnectTo:options:) はUI初期化前に呼ばれるため、画面遷移などをここで直接行うと不安定になるようです。
sceneDidBecomeActiveDeepLinkUniversal Links、通知レスポンスを処理することでクラッシュせずに実行することができました。

https://www.jakehao.com/scene-delegate-open-url?utm_source=chatgpt.com

以上です。

Discussion