SwiftUI 프로젝트에 AppDelegate, SceneDelegate 만들기
SwiftUI 프로젝트를 실행하면 AppDelegate.swift 와 SceneDelegate.swift 가 보이지 않는 것을 확인할 수 있다.
하지만 SwiftUI 앱이라 해도 외부 SDK나 APNs를 사용하려면 AppDelegate 와 SceneDelegate 가 필요할 수 밖에 없다.
그러면 우리가 직접 추가해서 연결해줘야 하지 않을까?
SwiftUI에는 왜 없을까?
sceneDelegate 가 iOS 13 부터 등장했지만, SwiftUI 프레임워크와 관련된 것은 아니었다.
기본적으로 UIKit의 App LifeCycle을 관리하기 위해 AppDelegate 와 SceneDelegate 가 존재한다.
그래서 XCode 13 부터는 새 프로젝트 생성 창에서 Lift Cycle 선택이 사라졌다. AppDelegate.swift 와 SceneDelegate 를 자동으로 생성하려면
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 을 받아와서 핸들링 할 수 있다.
아마 원래 AppDelegate 나 SceneDelegate 를 사용하는 것보다 이게 더 간단한 방식을 제공하는 것일 수도 있다.
•
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.delegateClass 를 MySceneDelegate 로 설정해줌으로써 MySceneDelegate 도 활용할 수 있다.
위처럼 설정해주면 어쩔 수 없는 상황에서도 AppDelegate 와 SceneDelegate 를 만들어내 이용할 수 있을 것이다.