티스토리 뷰

안녕하세요 Hani입니다.

이번에는 WWDC19에서 발표된 Architecting Your App for Multiple Windows를 바탕으로

AppDelegate와 SceneDelegate에 대하여 알아볼 거예요.

 

 

먼저 AppDelegate부터 살펴봅시당. ☺️

iOS13 이전에서의 AppDelegate는 두 가지 주요한 역할이 있었습니다.

 

첫 번째는 앱이 켜지고 꺼지는 이벤트를 애플리케이션에게 알리는 것이고,

두 번째는 UI의 상태 변화를 애플리케이션에 알리는 것이에요.

 

 

AppDelegate가 관리하는 UI는 다음과 같았습니다.

iOS13 이전에서는 하나의 애플리케이션은 하나의 UI 인스턴스만을 가지고 있었어요. 🥺

즉, 앱당 하나의 window만을 가지고 있었습니다.

 

 

iOS13 이전의 애플리케이션을 실행시킬 때는 AppDelegate의 이 메서드에서 window 객체를 할당했습니다.

 

 

iOS13 이전에는 하나의 애플리케이션이 하나의 프로세스와 하나의 UI 인스턴스만을 가지고 있었기 때문에 문제가 없었습니다.

 

iOS13부터는 하나의 애플리케이션이 여전히 하나의 프로세스를 갖지만 여러 화면을 가질 수 있기 때문에

애플리케이션이 window를 여러 개 지원할 필요가 생겼습니다. 🧐

 

 

따라서 AppDelegate의 역할 중 UI의 생명주기를 다루는 부분이 SceneDelegate로 옮겨졌습니다.

 

Scene은 하나의 window와 뷰컨을 포함하는 객체예요.

SceneDelegate 덕분에 더 이상 AppDelegate는 window를 비롯한 UI에 대하여 관여하지 않습니다. 👍

 

 

여기서의 scene은 뷰컨의 한 화면 단위인 scene과는 다른 아이입니다.

이름을 왜 같게 만들었는지는 모르겠지만..!

(뷰컨의 씬을 page라고 부르기도 한답니다.)

 

 

대신 AppDelegate에는 Session Lifecycle을 다루는 부분이 추가되었네요.

AppDelegate은 이제 씬의 생성과 삭제를 관리하는 역할을 맡습니다.

 

씬이 여러 개가 되면 윈도우도 여러 개가 되니까 멀티 윈도우, 멀티 씬이 만들어질 수 있어요. 😊

 

 

그럼 iOS13부터는 애플리케이션을 실행하면 어떻게 진행이 되느냐! 🧐

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

iOS13에서도 애플리케이션의 시작점은 여전히 AppDelegate입니다.

 

단, 소개된 메서드는 애플리케이션 실행될 준비를 마쳤을 때 호출되는데

iOS13 이전에는 이곳에서 Window 객체와 Window의 루트뷰컨을 할당했지만

iOS13 이후에는 SceneDelegate에서 하게 됩니다.

 

 

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

다음은 씬을 생성해주는 메서드가 호출이 됩니다.

 

 

 

씬만 만들었으니 아직 아무런 UI가 없습니다.

이제 SceneDelegate로 넘어가서 UI를 만들어줄 차례입니다.

 

 

AppDelegate에서 했던 작업들을 SceneDelegate로 옮겨왔습니다.

 

 

짜잔 🦀

 

 

 

다시 홈으로 이동하면 SceneDelegate에서 메서드가 차례대로 호출됩니다.

(원래는 AppDelegate에서 UI 생명주기를 다루던 메서드들)

 

 

그런데 Disconnect..! 백그라운드의 씬의 연결이 끊길 수도 있어요.

 

 

백그라운드로 들어간 씬은 시스템이 메모리를 위해 언제든지 연결을 끊어버릴 수 있습니다.

 

씬의 연결이 끊긴다는 것은 SceneDelegate도, SceneDelegate가 가진 window도, window의 루트 뷰컨 등등 몽땅..! 😡

 

 

하지만 메모리 형편이 살만해져서 씬이 나중에 다시 연결될 수 있기 때문에

백그라운드로 씬을 보내서 시스템이 씬을 끊어버리는 방법으로

사용자의 데이터나 상태를 영구적으로 삭제하는 것은 좋지 않습니다. 🥺

 

 

 

사용자가 정말로 애플리케이션을 꺼버리는 경우는 어떨까요?

 

 

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

이 때는 AppDelegate의 메서드가 호출되는데 씬과 관련된 데이터가 영구적으로 삭제됩니다. 

 

 

이제 우리는 UIApplicationDelegate를 채택한 AppDelegate에서 구현한 세 가지 메서드를 모두 알게 되었습니다. 😊

 

 

 

이제 State Restoration이라는 것에 대하여 알아볼 차례..

 

간단히 말하면 어떤 작업을 하다가 앱을 백그라운드로 보냈을 때,

다시 앱을 켜면 그 상태가 보존되어 있어서 작업을 이어서 할 수 있게 해주는 것이에요.  

 

 

예시를 봅시당. 

화면 4개를 켜놨는데

 

그중 두 화면은 백그라운드로 보냈다고 했을 때

백그라운드의 씬은 언제든지 시스템에 의해 날아갈 수 있죠?

 

다시 Road Trip과 Attendees를 화면에 불러올 때 초기 화면부터 시작하면 

기존에 해놨던 작업을 다시 해야하니까 좋지 않은 UX를 보여주게 됩니다. 🥺

 

 

 

그래서 마련한 씬 기반 State Restoration 

 

 

기존에는 State Restoration을 위해 뷰 계층구조를 encoding했지만

iOS13에서는 window를 다시 생성할 수 있는 State를 encoding하는 방식으로 작동합니다.

 

This is all based on NSUser activity as well. 

So, if your application leverages powerful technologies like spotlight search or handoff, 

you can use these same activities to encode the state of your app.

 It's also worth noting that in iOS 13,

 the state restoration archive that you give back to the system will match the same data protection class of the rest of your application. 

What does this look like in code? Well, in our scene delegate,

 we implement state restoration activity for scene and then I call a method that finds the most active relevant user activity form in the current window. 

Then we return that. After some time, when that scene re-enters the foreground and becomes connected,

 we check if the session contains a state restoration activity. 

If it does, we use that activity. If not, we can create a brand-new window without any state.

 This means that no matter what, our users will never notice when scenes get disconnected in the background because they shouldn't.

 

아유 몬소리야 엉엉..

 

 

이제 우리는 멀티 씬에 대한 개념을 알게 되었어요. ☺️

 

 

이때, 두 개 이상의 씬이 다른 화면을 갖게 할 수도 있지만 같은 화면을 갖도록 동기화하려면 어떻게 해야 할까요?

 

 

두 개 이상의 씬이 있을 때

 

 

한쪽 씬에서 입력을 가했을 때 다른 씬에서는 반응이 없는 모습입니다.. 🥺

 

 

위 채팅창에서의 모습은 UI가 사용자의 입력을 받았을 때, 뷰컨이 자체적으로 뷰를 갱신하고

업데이트할 사항을 모델이나 모델 컨트롤러에게 알려주는 형식입니다.

 

 

하나의 씬을 가지고 있었다면 문제없었겠지만 두 개 이상의 씬이라면 얘기가 달라집니다.

자기 씬의 뷰만 갱신하고 모델한테 넘겨주면 다른 씬의 뷰에는 반영이 안 되기 때문이에요. 🥺

 

 

코드로 보면 이런 모습입니다.

sender로부터 입력을 받았을 때, 뷰를 갱신하고 모델을 업데이트시키는..!

 

 

그럼 어떻게 해야 사용자로부터 입력을 받았을 때 여러 뷰에 동시에 반영이 될까요?

입력을 받았을 때 뷰컨이 직접 자신의 뷰를 갱신하는 과정을 생략하고 바로 모델에 넘겨주는 방식을 적용해야 합니다.

 

 

아까 뷰를 업데이트하는 코드가 사라졌죠?

 

 

사용자의 입력을 받고 뷰컨이 뷰를 직접 갱신하지 않으며 바로 모델을 업데이트 시킨 후,

모델이 업데이트가 끝나면 delegate, notification, combine 프레임워크 등을 통해

뷰컨이 모델의 변경사항을 가져갈 수 있도록 모델 업데이트를 알려주는 방식이라면

모든 씬의 뷰가 동일한 화면을 보여줄 수 있습니다. 👍

 

 

 

두 씬이 동기화되어 두 뷰가 같은 화면을 가질 수 있다. 😊

 

 


References

https://developer.apple.com/videos/play/wwdc2019/258/

https://developer.apple.com/forums/thread/665895

댓글