안녕하세요 iOS dev 루크입니다 😇
오늘은 SwiftUI 앱의 생명주기에 대해서 알아보려고 합니다.
iOS 14 업데이트와 함께 App
프로토콜이 AppDelegate
를 대체하게 되었습니다.
그에 따라서 생명주기의 활용법 또한 변경이 되었는데요
SwiftUI 프로젝트를 하게된다면 당연히 바뀐 생명주기도 다룰 줄 알아야겠죠 ㅎ
하나씩 살펴 보겠습니다.
@main
struct MyApp: App //- App protocol 을 채택하는 struct 의 이름은 자유로우나 일반적으로 ~~App 이라고 짓는다.
{
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
@main
앱의 시작점인 Entry Points 를 지정하는 역할을 합니다.
기존 AppDelegate
에서 진행되었던 리소스 초기화는
SwiftUI 에 들어와서는 init()
을 사용하게 됩니다
@main
struct MyApp: App
{
// 앱이 실행됨과 동시에 첫 view 생성전 미리 구성해야하는 즉 loading 과정 구현
let persistenceController = PersistenceController.preview
init() {
//앱 실행 전 초기화 작업 or 필요한 네트워킹
//앱이 실행되기도 전에 실행해야하는 서비스는 이렇게 구현
//앱을 실행하기위해 구성해야 하는 항목들은 init() 에 구현
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
body
프로퍼티에는 WindowGroup
객체가 자리잡고 있다. 이 객체는 cross-platform struct 로 여러개의 윈도우를 가진 scene 을 나타낸다. macOS iOS 등등에서 사용할 수 있으며, 앱내의 view 들의 계층구조를 담아내는 container 라고 생각하면된다.WindowGroup
내에는 앱의 첫번째 view
를 선언해준다. 위 코드에서는 BookList()
가 앱 실행시 처음으로 띄워질 view
가 된다.AppDelegate
에서 사용하던
applicationDidBecomeActive
백그라운드 → 포그라운드 이동 후applicationWillResignActive
포그라운드 → 백그라운드 이동 전applicationDidEnterBackground
포그라운드 → 백그라운드 이동 후들이 다른 방식으로 대체되었습니다
@Environment
프로퍼티 래퍼를 사용해 현재 앱의 생명주기 상태를 가져올 수 있습니다.
이렇게 가져온 값에 대해 .onChange(of:)
메서드를 사용해서 해당 생명주기의 대응할 수 있게 됩니다
import SwiftUI
@main
struct MyApp: App {
@Environment(\.scenePhase) var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { newScenePhase in
switch newScenePhase {
case .active:
print("App is active")
case .inactive:
print("App is inactive")
case .background:
print("App is in background")
@unknown default:
print("unexpected Value")
}
}
}
}
화면 기록 2022-04-03 오후 6.27.06.mov
ScenePhase
enum 은 3개의 상태를 나타냅니다.
active
앱이 포그라운로 올라왔을 때
inactive
앱 스위칭 ing
디바이스 화면 전경에 앱이 존재하지만 일시정지 상태이며, 이벤트도 받을 수 없는 상태를 의미
background
아이폰 홈 화면으로 나왔을 때
AppDelegate
가 필요할 때@UIApplicationDelegateAdaptor
프로퍼티 사용SwiftUI 에서도 그래도 AppDelegate
를 사용해야 하는 순간이 온다. 바로 UIkit
에 머물러있는 Swift 패키지들을 사용할 때(특히 FireBase🔥) 인데,,, (작성일 기준입니다)
@main
struct MyApp: App {
init() {
FirebaseApp.configure() // 🚨 불가능
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
이는 생각보다 간단히 해결이 가능합니다
//MyApp.Swift
import SwiftUI
class AppDelegate: NSObject, UIApplicationDelegate
{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
{
//패키지 초기화 작업 수행
FirebaseApp.configure() //👍🏻
return true
}
}
@main
struct MyApp: App
{
//아래 프로퍼티 래퍼를 활용
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
AppDelegate
클래스 선언NSObject
프로토콜 준수UIApplicationDelegate
프로토콜 준수 → Delegation 함수 사용가능 @UIApplicationDelegateAdaptor
프로퍼티 래퍼 사용 AppDelegate
클래스 타입 전달이렇게 해서 아직 App
protocol 에서 사용이 불가능한 기능들을 사용할 수 있답니다.
Ex. remote push notification
중요한 점은 AppDelegate
를 어떤용도로든 사용할 수 있게 되지만,
이는 어디까지나 App
구조체 내부에서 사용이 불가능한 기능에만 사용하는 것이 현명하겠네요 ㅎ
@main
struct MyApp: App
{
init() {
//앱 초기 설정 작업
}
var body: some Scene
}
이렇게 깔끔한데 굳이 AppDelegate
를 사용할 필요가 없지 않을까요? 😃
오늘 포스팅은 여기서 마무리하겠습니다 감사합니다 😊