👋

UIApplicationDelegate内の一部のメソッドが動作しない

2024/09/07に公開
1

こうなればいいなと思った実装

import SwiftUI
import UIKit

@main
struct demoApp: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
+   func applicationWillEnterForeground(_ application: UIApplication) {
+       // バックグラウンドからフォアグラウンドに復帰したとき
+   }

+   func applicationDidEnterBackground(_ application: UIApplication) {
+       // フォアグラウンドからバックグラウンドに移行するとき
+   }
}

でも、UIApplicationDelegateのライフサイクルメソッドが動作しない

一旦、設定内容を確認しよう。もし、SceneDelegate関連の記述が使われている場合は、排除する。

AppDelegateからScene関連の記述を排除

import UIKit

@main
class AppDelegate: NSObject, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
- 
-   // MARK: UISceneSession Lifecycle
-
-    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
-        // Called when a new scene session is being created.
-        // Use this method to select a configuration to create the new scene with.
-        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
-    }
-
-    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
-        // Called when the user discards a scene session.
-        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
-        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
-    }
-
-
}

AppDelegateにwindowプロパティを追加

SceneDelegateに置かれていた UIWindow型のプロパティであるwindowプロパティはAppDelegateに移してみる。

import UIKit

@main
class AppDelegate: NSObject, UIApplicationDelegate {

+   var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        return true
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // バックグラウンドからフォアグラウンドに復帰したとき
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // フォアグラウンドからバックグラウンドに移行するとき
    }
}

Info.plist から UIApplicationSceneManifest をすべて消す

(省略)
:
- 	<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>
:
(省略)

んーーこれでもダメ

UIApplicationDelegateAdaptor設定しても、AppDelegate のすべてのメソッドが呼ばれるわけではないらしい。公式にも書いてありました。
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground/#discussion

代わりにNotificationCenterを使う。
https://developer.apple.com/documentation/foundation/notificationcenter

import SwiftUI
import UIKit

@main
struct demoApp: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
+               .onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
+                   // フォアグラウンドからバックグラウンドに移行するとき
+               }
+               .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
+                   // バックグラウンドからフォアグラウンドに復帰したとき
+               }
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
}

SceneDelegateを使用している場合

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    func sceneWillEnterForeground(_ scene: UIScene) {
        // バックグラウンドからフォアグラウンドに入る直前に行う処理
    }
 
    func sceneDidEnterBackground(_ scene: UIScene) {
        // アプリがバックグラウンドに入るときに行う処理
    }
}

Discussion