SwiftUI와 SceneDelegate

SwiftUI로 만든 앱은 대부분 SceneDelegate를 사용하여 UI를 구성합니다.

먼저 Application Scene Manifest는 아래와 같습니다.

프로젝트를 생성하면 기본적으로 위와 같습니다. 한 가지 눈에 띄는 것은 Storyboard 이름이 설정되어 있지 않는 것입니다. 그리고 만약 multi window를 지원하려면 Enable Multiple Windows의 값을 YES로 변경해야 합니다.

그리고 우리는 AppDelegate에 대해서는 다루지 않을 것 입니다. 왜냐하면 아주 기본적인 시나리오이기 때문에 그냥 true만 리턴할 것이기 때문입니다.

다음으로는 SceneDelegate 입니다. 이미 얘기했던 것처럼 SceneDelegate는 앱의 scene을 세팅하는 책임을 가지고 있습니다.

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let contentView = ContentView()
        
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }
    .
    .
    .
}

위 코드를 하나씩 살펴봅시다.

  • 먼저 scene(_:willConnectTo:options:) 함수는 새로운 scene이 앱에 추가될 때 호출됩니다. 여기서 scene 객체와 scene session을 제공합니다. UIWindowScene 객체는 앱에 의해 자동으로 만들어지므로 수동으로 수행하면 안됩니다.
  • 그런다음에 window 속성도 사용됩니다. 앱은 여전히 UIWindow 객체를 사용하지만, 이제는 scene의 일부분일 뿐입니다. 코드에서 if let 블럭 안에서 UIWindow 객체가 초기화되는 방법을 명확히 볼 수 있습니다.
  • 그 다음으로 rootViewController가 할당되고 window에 앞서 만들어진 window를 할당합니다. 그리고 makeKeyAndVisible()을 통해서 window를 UI 앞에 놓습니다.
  • SwiftUI에서 ContentView가 만들어지고 UIHostingController를 통해 rootViewController에 추가됩니다. 이 controller는 SwiftUI 기반의 View를 화면에 표시하는데 사용됩니다.
  • 마지막으로 중요한 것은 UIScene 타입의 파라미터가 UIWindowScene 타입의 객체라는 것이다.

이 모든게 복잡해 보일지 모르지만, 큰 그림으로 보면 꽤 간단합니다.

  • scene(_:willConnectTo:options:) 함수가 호출될 때 SceneDelegate가 scene을 구성한다.
  • AppDelegate와 Application Scene Manifest는 Storyboard와 관련없는 기본 구성을 가지고 있습니다.
  • scene(_:willConnectTo:option:) 함수는 SwiftUI의 View를 만들어 UIHostingViewController에 넣고, 다시 이걸 rootViewController에 할당합니다. 그리고 이를 앱의 최상단 window에 할당합니다.

좋습니다. 계속해서 알아봅시다.

Storyboard와 SceneDelegate

현재까지는 Storyboard가 iOS 앱을 위한 UI를 만드는 효과적인 방법이었습니다. iOS 13이 나온 현재까지도 다르지 않습니다. 물론 시간이 흐를수록 SwiftUI를 사용한 앱을 더 많이 보겠지만, 어쨌든 지금은 Storyboard가 훨씬 대중적입니다.

흥미롭게도 SceneDelegate와 Storyboard를 함께 사용함에 있어서 우리는 어떤 것도 할 필요가 없습니다. 프로젝트를 만들면 모든 것이 이미 완성되어 있기 때문입니다.

이게 전부입니다. 정말 짧죠? 이에 대해서는 더 다룰 것이 없습니다…

Programmatically하게 SceneDelegate 사용하기

많은 개발자들이 UI를 Programmatically하게 사용합니다. 만약 Storyboard를 사용하지 않고 개별 XIB를 사용하는 경우에는 어떻게 해야할까요?

우선 AppDelegate와 Application Scene Manifest는 우리가 기대했던 것과 똑같습니다. 우리가 Storyboard를 사용하지 않는다면 initial view controller를 SceneDelegatescene(_:willConnectTo:option:) 함수 안에서 세팅해야 합니다.

다음처럼 말이죠.

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            // ------
            let timeline = TimelineViewController()
            let navigation = UINavigationController(rootViewController: timeline)
            window.rootViewController = navigation
            // ------
            self.window = window
            window.makeKeyAndVisible()
        }
    }
    .
    .
    .
}

꽤 간단하죠? SceneDelegate의 핵심은 이처럼 AppDelegate의 특정 부분을 SceneDelegate로 옮긴 것 입니다.


처음 번역을 시작할 때에는 글이 꽤 길기에 원하는 내용이 있을 줄 알았는데, 막상 끝까지 번역해보니 제가 원하는 내용은 없었네요. 도움되는 내용도 있었으나 아쉬운 부분도 있어서 추후 다시 한 번 비슷한 내용의 포스팅을 해볼 예정입니다.