iOS13부터 AppDelegate는 AppDelegate와 SceneDelegate로 분기되었다. 오늘은 분기된 이후의 각자 맡은 일과 동작방식 및 순서에 대해 알아보려고 한다.
iOS13부터 AppDelegate가 하는 일?
이전에는 앱이 foreground에 들어가거나 background로 이동할 때 앱의 상태를 업데이트하는 등의 앱의 주요 생명 주기 이벤트를 관리했었지만 SceneDelegate로 위임되면서 현재 하는 일은 이하 5가지이다.
- 앱의 가장 중요한 데이터 구조를 초기화하는 것
- 앱의 scene을 환경설정(Configuration)하는 것
- 앱 밖에서 발생한 알림(배터리 부족, 다운로드 완료 등)에 대응하는 것
- 특정한 scenes, views, view controllers에 한정되지 않고 앱 자체를 타겟하는 이벤트에 대응하는 것(앱 시작, 종료)
- 애플 푸쉬 알림 서비스와 같이 실행시 요구되는 모든 서비스를 등록하는것
정리하면, 앱 실행 및 종료와 관련된 Process Life Cycle은 AppDelegate에서, 앱이 Foreground와 Background 상태에 있을 때 상태 전환과 관련된 UI Life Cycle은 SceneDelegate에서 관리한다.만약 iOS12 이하 버전에 대응하기 위해 SceneDelegate를 사용하지 않도록 설정하면 예전처럼 AppDelegate가 모든 Life Cycle에 대한 관리 책임을 갖지만, 그렇지 않으면 UI Life Cycle은 SceneDelegate를 통해 관리해야 한다.
AppDelegate와 SceneDelegate
AppDelegate가 우측처럼 AppDelegate & SceneDelegate로 나누어짐에 따라 AppDelegate에서 Session LifeCycle이 AppDelegate에 추가 되었다.
Session LifeCycle에는 두 메소드가 존재한다.(Session Created, Session Discarded)
각각의 메소드는 Scene Session이 생성되거나 삭제될 때 AppDelegate에 알리는 역할을 한다.
Scene Session은 앱에서 생성한 모든 scene의 정보를 관리한다.
너무 생소한 용어들이 많다. Scene은 뭐고 Scene Session은 또 뭘까?
Scene?
UIKit는 UIWindowScene 객체를 사용하는 앱 UI의 각 인스턴스를 관리한다.
Scene에는 UI의 하나의 인스턴스를 나타내는 windows와 view controllers가 들어있다. 또한 각 scene에 해당하는 UIWindowSceneDelegate 객체를 가지고 있고, 이 객체는 UIKit와 앱 간의 상호 작용을 조정하는 데 사용된다.
Scene들은 같은 메모리와 앱 프로세스 공간을 공유하면서 서로 동시에 실행되기때문에 결과적으로 하나의 앱은 여러 scene과 scene delegate 객체를 동시에 활성화할 수 있다!
Scene Session?
UISceneSession 객체는 scene의 고유의 런타임 인스턴스를 관리한다. 사용자가 앱에 새로운 scene을 추가하거나 프로그래밍적으로 scene을 요청하면, 시스탬은 그 scene을 추적하는 session 객체를 생성하는것이다. 그 session에는 고유한 식별자와 scene의 구성 세부사항(configuration details)가 들어있다.
UIKit는 session 정보를 그 scene 자체의 생애(life time)동안 유지하고 app switcher에서 사용자가 그 scene을 클로징하는 것에 대응하여 해당 session을 파괴한다.
session 객체는 직접 생성하지않고 UIKit가 앱의 사용자 인터페이스에 대응하여 생성된다.
Secene LifeCycle 요약
Session LifeCycle에는 두개의 종이 있다. 하나는 Scene이 생겼을 때 치는 종, 하나는 Scene이 삭제되었을 때 치는 종. 종을 치는 이유는 AppDelegate에게 그 사실을 알려주기 위함이다(종 = 메소드).
그 종들을 치는 주체가 Scene Session이다. Scene Session은 Scene의 모든 행동들을 지켜보고 있으며 생성과 삭제가 될 때 마다 종을 쳐서 AppDelegate에게 그 사실을 알려주게 된다.
이 글을 함께 읽으면 더 좋을 것 같다.
AppDelegate와 SceneDelegat 메소드들이 실행되는 순서
[call stack]
green: App Delegate / blue: Scene Delegate
앱 클릭 → didFinishLaunchingWithOptions → configurationForConnecting → willConnectTo session(아직 화면에는 앱이 안뜬 상태)
→ scene(_:willConnectTo)에서, window = UIWindow(windowScene: scene as! UIWindowScene)(화면에 앱이 등장) →
willResignActive, didEnterBackground → didDisconnect(앱 화면을 명시적으로 종료할 시 ) →
→ didDiscardSceneSceneSessions: (scene이 didDisconnect됐을 경우.)(유저의 포커스에서 벗어난 후 다시 포커스를 받은 경우 데이터를 유지 하기 위해, 이곳에서 복구관련 정보를 획득 = 한 앱을 여러 화면 띄울 수 있음)
[AppDelegate의 메서드들]
func application (_ : didFinishLaunchingWithOptions :)-> Bool
- 앱 시작시 앱 설정이 완료될때 호출.
- iOS13 이전에는 이 메서드를 통해 UIWindow 개체를 구성하고 ViewController인스턴스를 할당했지만,iOS13 부터 애플리케이션에 장면이 있는 경우 AppDelegate는 더이상 이를 처리할 책임이 없고 SceneDelegate로 이동된다.
func application (_ : configurationForConnecting : options :)-> UISceneConfiguration
- 새 장면이나 새창이 필요할 때마다 호출된다.
- 이 메서드는 앱 시작시 호출되지 않고 새 장면 또는 새 창을 가져야 하는 경우에만 호출된다.
func application (_ : didDiscardSceneSessions :)
- 멀티 태스킹 창에서 스와이프 하는것과 같이 장면을 삭제할 때 또는 프로그래밍 방식으로 앱 제거시 호출된다.
[SceneDelegate의 메서드들]
AppDelegate의 UIWindow와 관련된 것은 이제 SceneDelegate의 UIScene입니다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
- UI창을 만들고 root view controller 를 설정하고 "설정한 창을 ‘키’ 창으로 만든다".
= "같은 수준 이하의 다른 모든 창앞에 해당 창을 배치한다." - 기존에 application(_:didFinishLaunchingWithOptions:)에서 했던 UIWindow 생성 작업을 이 method에서 할 수 있다.
- 새로운 화면 객체가 앱에 추가 될 때마다 호출된다.
func sceneDidDisconnect(_ scene: UIScene)
- iOS 에서는 리소스를 확보하기위해 앱의 Scene이 백그라운드로 전환시마다 Scene를 완전 폐기할지 결정할 수 있다. 이것이 앱이 종료되거나 실행되지 않음을 의미하는것은 아니다. Scene만 Session에서 연결해제되고 활성화 되지 않는것이다.
- iOS에선 사용자가 특정 Scene을 포그라운드로 전환시 세션에 다시 연결하도록 결정 할 수 있다.
- 이 메서드는 사용하지 않는 리소스를 삭제하는데도 사용할 수 있다.
func sceneWillResignActive(_ scene: UIScene)
- 앱이 백그라운드로 전환시 실행된다.
func sceneWillEnterForeground(_ scene: UIScene)
- 백그라운드에서 포그라운드로 전환시 실행된다.
func sceneDidBecomeActive(_ scene: UIScene)
- sceneWillEnterForeground 메서드 다음에 호출된다.
- 장면이 설정되고 표시할 준비가 되었음을 알려준다.
func sceneDidEnterBackground(_ scene: UIScene)
- sceneWillEnterForeground 이후에 실행된다.
- 백그라운드에서 포그라운드로 전환 완료시 실행된다.
도움을 준 좋은 블로그