AppDelegate, SceneDelegate

Choong Won, Seo·2024년 5월 19일

TIL

목록 보기
1/7
post-thumbnail

CodeBase UI를 작성할 때도, Framework configure()를 할 때에도 하라는대로 하기만 했지 정확히 각각이 어떤 역할을 하고 있는지 명확히 알지 못하는 것 같아서 이번 기회에 정리를 해보고자 한다.

iOS 13이전에는 AppDelegate가 launch, foregrounding, backgrounding 등 App Life-Cycle을 관리하는 책임을 모두 갖고 있었다. 그러나 iOS 13이후에는 일부 역할을 SceneDelegate에게 넘겨주었다.

UI Structure

그렇다면 가장 먼저, Scene이 뭐고, 개념적으로 어떤 변화가 나타났는지부터 살펴보자.

UIScreen: hardware-based display, 즉 iPhone, iPad 등의 화면 그 자체를 지칭하는 클래스이다. 물리적인 스크린 위에 UIWindowScene을 여러 개 배치시킬 수 있다고 생각하면 된다.

UIWindowScene: 이 친구가 가장 이해하기가 어려웠는데, iOS13에서 바뀐 점들 때문에 용어의 혼동이 많이 있어서였던 것 같다. 일단 iOS13에서 바뀐 내용은 이렇다.

iOS 13부터는 window 개념이 scene으로 대체되고, 하나의 앱에 여러 scene을 가질 수 있다.

(window보다 더 상위개념이 생겼다 정도로 이해하면 될 것 같다.)

Scene

iPadOS가 생기고, 그로 인해 Split View를 지원하고, 또 iOS에서도 pip등 멀티 스크린 환경을 지원함에 따라 그 전에는 하나의 window가 사실상 하나의 앱을 담당하던 것에서 새로운 개념이 필요하게 되었다.

그렇게 생겨난 개념이 scene이다. 위에 사진과 같이 하나의 앱(UIScreen)에서 파랑, 보라 이렇게 다수의 scene을 관리해주어야 한다.

→ 이 scene을 관리하는 ‘객체’가 바로 UIWindowScene!!

guard let windowScene = (scene as? UIWindowScene) else { return }

보통 CodeBase UI를 작성할 때 적는 코드에서 scene을 UIWindowScene으로 타입캐스팅을 하는 것을 보면 대충 짐작을 할 수 있다.

(다른 말이긴 한데 중간에 궁금해서 찾아본 결과 이렇게 같은 앱에서 나온 여러 개의 Scene들은 같은 메모리와 프로세스 공간을 공유한다고 한다.) → Scene마다 정보가 달라지면 안되므로

UIWindow: View들을 담는 컨테이너(배경막)이자 이벤트를 전달해주는 매개체이다.

또한 UIWindow 자체는 시각적인 요소를 전혀 포함하고 있지 않다. 한 개 이상의 View를 포함할 수 있는 비어있는 컨테이너다. 또한, 새로운 컨텐츠를 보여주기 위해 Window를 교체하는 대신 포함한 View를 교체한다.

(※ 그림에서 UIWindowScene에 1개 이상의 UIWindow가 있는 것을 보고 예시가 떠오르지 않아 좀 머리가 복잡했는데, 이론상 다수의 window를 가질 수도 있다.. 정도로 이해하면 될 것 같다.)

UIView: 화면의 직사각형 모양을 관리하는 객체, 앱과 사용자가 상호작용하는 주요 방법이다.

사실 UIView보다는 위의 개념들을 더 자세히 공부하기 위해 작성하고 있으므로 빠르게 넘어간다.

UI strcture를 바탕으로 직접 hierachy를 살펴보면 더 이해가 빠르다.

LifeCycle

앞서 알아본 UI Structure의 변화에 따라 UILifeCycle은 더이상 AppDelegate에서 관리하지 않게 되었다.

SceneDelegate가 이를 대신하고, AppDelegate에서는 Session Lifecycle만 관리한다.

AppDelegate

AppDelegate 새 역할: Scene Session생성(Created)되거나 삭제(Discarded)될 때 동작하는 메소드가 추가되었다.

AppDelegate에서 MARK: UISceneSession Lifecycle로 된 Default 메소드 2개를 볼 수 있다.

이들이 관리하는 것이 바로 SceneSession이라고 유추를 해볼 수 있다.

//새 Scene을 만들 때 UIKit을 위한 Configuration Data를 가져온다.
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

//사용자가 App Switcher에서 하나 이상의 앱 Scene을 닫았음을 AppDelegate에게 전달.
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
  
}

그래 Scene이 뭔지는 위에서 알아봤고, 그럼 Scene Session이란 무엇일까?

Scene Session: Scene 고유의 런타임 인스턴스를 관리한다.

첫 번째 함수에서 사용자가 앱에 새로운 Scene을 추가하거나 프로그래밍적으로 Scene을 요청하면 시스템은 그 Scene을 추적하는 Session객체를 생성한다. → 여기에 Session의 식별자와 Configuration이 들어있다.

UIKit은 Scene의 생애동안 Session정보를 유지한다.

두 번째 함수에서 App Switcher에서 사용자가 Scene을 클로징하는 것에 대응하여 그 Session을 파괴한다.

iOS 12이전은 App 기반 라이프 사이클 이벤트였다. UI와 App은 별개의 라이프 사이클을 가지지 않고, 앱의 상태에 따라 앱 전체의 UI가 바뀌도록 상호작용한다.

하지만 iOS 13이후에는 Scene 기반 라이프 사이클 이벤트가 도입된다. 바로 앞에서 살펴봤던 Scene Session과 연관지어 생각하면 조금 이해하기가 편한데,

앱 자체에서 여러 Scene을 표시할 수 있다보니까, Unattatched나 Background에 Scene이 있더라도 Suspend되기 전엔 Scene이 종료되지 않는다.

시스템이나 유저가 새로운 Scene을 요청하면 UIKit은 이를 생성(Session 생성)하고, 연결하지 않은 채로 둘 수도 있고(Session 정보 유지), 2개 이상의 Scene을 화면에 띄워 동시에 보이게 만들 수도 있다.

→ 그래서 이런 변화들로 인해 Scene 기반 라이프 사이클 이벤트에서의 모든 Scene 생명 주기에 관한 역할들은 SceneDelegate가 떠맡게 되었다.

AppDelegate에 남아있는 마지막 하나의 역할만 살펴보고 SceneDelegate로 넘어가도록 하자.

ProcessLifeCycle

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.   
    return true
}

AppDelegate에 남아있는,didFinishLaunchingWithOptions, willFinishLaunchingWithOptions등등은 Scene과 상관없이 ’App’자체가 처음 실행되고 종료될 때를 다루는 메소드라고 이해하면 될 것 같다.

이를 ProcessLifeCyle이라고 한다.

SceneDelegate

드디어 SceneDelegate, 먼저 가장 많이 쓰고 있는 scene(will connectTo) 함수부터 알아보자.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let _ = (scene as? UIWindowScene) else { return }
}

CodeBase UI를 작성할 때, window를 연결하고 KeyAndVisible을 설정할 때 자주 쓰는 이 함수는 Scene이 앱에 추가될 때 호출된다.

각각의 Scene마다 RootViewController를 설정해주거나 DarkMode등을 비활성화 할 때도 사용된다.

func sceneDidDisconnect(_ scene: UIScene) {
		//시스템이 자원을 확보하기 위해 disconnect하는 상황일 수도
		//생성이 쉬운 데이터는 돌려주고, 사용자 input과 같이 어려운 데이터는 갖고 있게
    print("sceneDidDisconnect")
}

func sceneDidBecomeActive(_ scene: UIScene) {
		//처음 Active, 혹은 inactive -> active 일 때
		//멈춘 task를 재실행할 때 사용 가능
    print("sceneDidBecomeActive")
}

func sceneWillResignActive(_ scene: UIScene) {
		//전화가 올 때 등 임시 interruption
    print("sceneWillResignActive")
}

func sceneWillEnterForeground(_ scene: UIScene) {
		//처음 Active 상태가 되거나 background -> foreground가 될 때
    print("sceneWillEnterForeground")
}

func sceneDidEnterBackground(_ scene: UIScene) {
		//foreground -> background
		//다시 돌아올 때 복원할 수 있도록 정보 저장, 데이터 저장, 공유 자원 돌려주기 등 가능
    print("sceneDidEnterBackground")
}

나머지 함수들 또한 Scene의 생명주기에 따라 고유한 라이프사이클을 가진다. App이 나타나고 꺼질 때 한 번만 나타나는 AppDelegate의 함수와 다르게 UI에 나타나는 Scene에 따라서 계속 불릴 수 있는 함수여서 각각의 상황에 맞게 사용자가 기억해야 하는 메모리들을 임시저장하거나, 보안을 위해 Disconnect되기 전에 자동로그아웃을 진행하는 등의 작업을 각각 배정해줄 수 있을 것 같다.

Enable Multiple Windows옵션을 통해 실제로 Multi Scene을 가진 앱을 만들어 볼 수 있다.

AppDelegate와 SceneDelegate로 나눠져야 했던 필수 배경부터 학습하고 알아보니 그냥 외우는 것보다 더 이해가 잘 된 것 같다.

참조

[iOS] 윈도우란? (feat. UIWindow)

[iOS 앱개발] UIView 알아보기

[iOS] Scene, Window, View에 대한 아주 기초적인 정리

[iOS] iOS13이후의 AppDelegate와 SceneDelegate

[iOS] SceneDelegate & AppDelegate의 역할

profile
UXUI Design Based IOS Developer

0개의 댓글