Search
Duplicate

SwiftUI 프로젝트에 AppDelegate, SceneDelegate 만들기

생성일
2024/08/07 02:21
태그
지혜로운 회사생활
틈틈히 자기개발

SwiftUI 프로젝트에 AppDelegate, SceneDelegate 만들기

SwiftUI 프로젝트를 실행하면 AppDelegate.swiftSceneDelegate.swift 가 보이지 않는 것을 확인할 수 있다.
하지만 SwiftUI 앱이라 해도 외부 SDK나 APNs를 사용하려면 AppDelegateSceneDelegate 가 필요할 수 밖에 없다.
그러면 우리가 직접 추가해서 연결해줘야 하지 않을까?

SwiftUI에는 왜 없을까?

sceneDelegate 가 iOS 13 부터 등장했지만, SwiftUI 프레임워크와 관련된 것은 아니었다.
기본적으로 UIKit의 App LifeCycle을 관리하기 위해 AppDelegateSceneDelegate 가 존재한다.
그래서 XCode 13 부터는 새 프로젝트 생성 창에서 Lift Cycle 선택이 사라졌다. AppDelegate.swiftSceneDelegate 를 자동으로 생성하려면
Interface를 Storyboard 를 선택해야 한다.
iOS 14부터 사용되는 App 프로토콜로 기본 Scene을 구축한다.
@main 을 달고 App 프로토콜을 채택한 ProjectInitApp 이 앱의 root로서 굴러간다.

SwiftUI에서는 App Life Cycle은 이 상태에서 어떻게 관리 해야할까?

방법은 두 가지가 있다.

1. SwiftUI에서 제공하는 기능으로 흉내내기

화면 Phase
@Environment(\.scenePhase) var scenePhase ... .onChange(of: scenePhase) { phase in switch phase { case .active: print("켜짐") case .inactive: print("꺼짐") case .background: print("백그라운드에서 돌아가는 중") } }
Swift
복사
위처럼 scenePhase 를 받아와서 App의 WindowGroup에 .onChange 를 달아줘서 앱 상태의 변화를 인지하고,
그에 따른 적절한 명령을 할 수 있다.
딥링크가 들어온다
WindowGroup { ContentView() .onOpenURL { url in print("URL: \(url)") }
Swift
복사
url을 통해 앱으로 진입할 경우 이렇게 url 을 받아와서 핸들링 할 수 있다.
아마 원래 AppDelegateSceneDelegate 를 사용하는 것보다 이게 더 간단한 방식을 제공하는 것일 수도 있다.
userActivity 받아오기
.onContinueUserActivity("what") { userActivity in if let thing = userActivity.userInfo?["something"] as? String { print("Get \(thing)") } }
Swift
복사
푸시 알림 등을 통해 앱으로 진입하면 userActivity로 들어오게 되는데, 이 메서드를 사용하면 userActivity를 활용할 수 있다.

2. AppDelegate, SceneDelegate 만들기

AppDelegate.swift
class MyAppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // ... return true } }
Swift
복사
우선 NSObject 타입으로 객체를 만들어준다.
그리고 AppDelegate 의 프로토콜인 UIApplicationDelegate 를 채택해준다.
UIApplicationDelegate 가 제공하는 많은 메서드들을 이용해 앱의 프로세스 처리를 진행할 수 있다.
당장 위 코드 처럼, application(_:didFinishLaunchingWithOptions:) 메서드를 이용해 앱이 켜지고 나서 어떤 명령을 처리할지 설정할 수 있다.
@main struct ProjectInitApp: App { @UIApplicationDelegateAdaptor var delegate: MyAppDelegate var body: some Scene { WindowGroup { ContentView() } } }
Swift
복사
UIApplicationDelegateAdaptor 프로퍼티 래퍼를 이용해 MyAppDelegate 를 넣어주었다.
UIApplicationDelegateAdaptor 는 애플에서 SwiftUI에서도 AppDelegate 를 사용할 수 있도록 제공한 프로퍼티 래퍼로,
NSObject와 UIApplicationDelegate를 충족해야 하는 것으로 되어있다.
AppDelegate 는 SwiftUI와 Integration 하는데 성공
SceneDelegate.swift
UI LifeCycle을 담당
class MySceneDelegate: NSObject, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let _ = (scene as? UIWindowScene) else { return } } func sceneDidDisconnect(_ scene: UIScene) { } func sceneDidBecomeActive(_ scene: UIScene) { } func sceneWillResignActive(_ scene: UIScene) { } func sceneWillEnterForeground(_ scene: UIScene) { } func sceneDidEnterBackground(_ scene: UIScene) { } }
Swift
복사
기존에 초기로 생성되던 SceneDelegate.swift 를 NSObject 타입으로 변경하고 UIWindowSceneDelegate 를 채택했다.
여기 있는 메서드 이름들을 보면 알듯이, 앱 화면이 활성화되거나, 백그라운드로 가거나에 따라 실행되는 메서드들이 다르다.
그리고 MyAppDelegate 로 연결
class MyAppDelegate: NSObject, UIApplicationDelegate { // ... func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguraiton { let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role) sceneConfig.delegateClass = MySceneDelegate.self return sceneConfig } }
Swift
복사
sceneConfig.delegateClassMySceneDelegate 로 설정해줌으로써 MySceneDelegate 도 활용할 수 있다.
위처럼 설정해주면 어쩔 수 없는 상황에서도 AppDelegateSceneDelegate 를 만들어내 이용할 수 있을 것이다.