본 글은 LearnAppMaking.com의 을 번역한 글임을 알리고(Thanks👏) 시작하겠습니다.

Xcode 11부터 프로젝트를 생성하게 되면 AppDelegateSceneDelegate가 자동으로 생성됩니다. 이는 SwiftUI와는 관계가 없습니다. 정확히는 SwiftUI를 사용하지 않는 프로젝트에서도 동일하게 AppDelegateSceneDelegate가 생성됩니다. 그렇다면 새롭게 추가된 SceneDelegate는 정확히 무슨 역할을 할까요?

이 글에서는 iOS 13과 Xcode 11의 변화점에 대해 다룰 것 입니다. 그 중에서 특히 SceneDelegateAppDelegate에 대해서 집중적으로 다뤄볼 예정입니다. 그리고 나서 이어지는 파트 2에서 이 변화가 SwiftUI와 Storyboard에 어떻게 영향을 주는지 알아볼 예정입니다.

우리는 이 글을 통해 이런 것들을 배울 수 있습니다.

  • SceneDelegateAppDelegate
  • SceneDelegateAppDelegate을 어떻게 앱에 적용시킬지
  • SceneDelegate와 함께 Programmatically하게 설정하는 방법
  • Storyboard와 SwiftUI에서 SceneDelegate를 사용하는 방법

준비됐나요? 바로 시작합시다.

AppDelegate

우리는 이미 AppDelegate에 익숙합니다. 이것은 iOS 앱의 시작 지점입니다. application(_:didFinishLaunchingWithOptions:)은 앱이 시작될때 시스템이 첫번째로 호출하는 함수입니다.

AppDelegateUIApplicationDelegate 프로토콜을 채택하고 있으며, UIApplicationDelegateUIKit의 일부입니다. 곧 알게 되겠지만, iOS 13에서는 AppDelegate의 역할이 바뀝니다.

일반적으로 iOS 12까지는 이렇게 사용했습니다.

  • rootViewController를 설정
  • 앱의 설정 및 기타 세팅 구성(예: 클라우드 서비스, 로깅 등)
  • 푸시 알림 핸들러 등록과 전송된 푸시에 응답
  • 백그라운드, 재시작 등 앱 라이프 사이클 이벤트에 대응

스토리보드만 사용하는 AppDelegate는 놀랍도록 단순합니다. 왜냐하면 그저 true만 리턴하기 때문이죠.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    return true
}

반면 XIB를 이용하는 간단한 앱을 예로 들면 rootViewController 세팅을 위해 다음과 같은 코드를 사용하기도 합니다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {   
    let timeline = TimelineViewController()
    let navigation = UINavigationController(rootViewController: timeline)

    let frame = UIScreen.main.bounds
    window = UIWindow(frame: frame)

    window!.rootViewController = navigation
    window!.makeKeyAndVisible()

    return true
}

위 코드에서 UINavigationControllerrootViewControllerTimeLineViewController를 지정했고, 다시 이것을 UIWindow의 속성인 rootViewController에 할당했습니다. UIWindowAppDelegate가 가지고 있는 속성이며, 앱이 가지고 있는 window 입니다.

앱의 window는 중요한 개념인데, 본질적으로 window는 앱 자체이며, 대부분의 iOS 앱은 오직 하나의 window만 가지고 있습니다. 이 window는 UI를 저장하고, 이벤트를 뷰에 전달하며, 앱의 콘텐츠를 나타내는 배경을 제공합니다. 여전히 window에 대한 이해가 어렵다면, iPhone의 홈버튼을 2번 누르거나 X 이후의 iPhone에서는 홈 인디케이터를 아래에서 위로 쓸어넘겨 App Swticher를 실행해보세요. 여기에서 현재 실행중인 앱들을 볼 수 있습니다. 여기서 보여지는 것들이 각 앱의 window 입니다.

SceneDelegate

iOS 13부터 AppDelegate의 몇가지 역할을 SceneDelegate가 하게 됐습니다. 가장 중요한 것은, window 개념이 scene으로 대체된 것입니다. 앱은 하나 이상의 scene을 가질 수 있고, 이 scene이 UI와 앱의 콘텐츠를 나타내는 배경을 제공합니다. iOS와 iPadOS에서 multi window 앱을 만들 수 있게 되면서 여러개의 scene으로 구성된 앱을 만들 수 있는 것은 상당히 흥미습니다. 예를 들면 워드 프로세서 앱의 문서가 각각의 scene을 가지고 있다고 가정하면, 사용자는 이 scene의 복사본을 만들 수 있고 하나의 앱의 여러 인스턴스를 효과적으로 실행할 수 있습니다. (이 부분은 잘 이해가 안되네요. 원문을 읽어보길 추천합니다.)

다음 세 가지가 SceneDelegate의 특징이 드러나는 가장 잘 드러나는 부분입니다.

  1. 새로운 iOS 프로젝트는 자동으로 생성되는 SceneDelegate를 가지고 있고, active, resign, disconnect 등의 라이프 사이클 이벤트가 여기에 포함됩니다.
  2. AppDelegate에는 scene sessions과 관련된 application(_:configurationForConnecting:options:)application(_: didDiscardSceneSessions:)라는 새로운 두 가지 함수가 추가됐습니다.
  3. Info.plist는 Application Scene Manifest를 갖게 됐고, 여기에는 각각의 scene이 포함하는 클래스와 delegate, 스토리보드이름들이 포함됩니다.

이제부터 하나씩 자세히 알아봅시다.

1. SceneDelegate

SceneDelegate에서 가장 중요한 함수는 scene(_:willConnectTo:options:) 입니다. 이 함수는 전통적인 AppDelegateapplication(_:didFinishLaunchingWithOptions:)의 기능과 유사하다고 볼 수 있습니다. 이 함수는 앱에 scene에 추가될때 호출되기 때문에 scene을 구성하기 가장 알맞은 지점입니다. 위 코드에서는 수동으로 view controller 스택을 설정하는데, 이것에 대해서는 나중에 알아보겠습니다.

일단 여기서 가장 중요한 것은 하나의 delegate가 모든 scene에 응답한다는 점입니다. 즉 하나의 delegate를 사용하여 앱의 모든 scene을 구성해야 합니다.

그리고 SceneDelegate에는 다음의 기능도 있습니다.

  • sceneDidDisconnect(_:) 함수는 scene이 앱에서 disconnect 될 때 호출됩니다. (추후에 다시 connect될 수 있습니다.)
  • sceneDidBecomeActive(_:) 함수는 scene이 사용자와 상호작용을 시작할 때 호출됩니다. (예로 App Switcher에서 앱을 선택했을 때)
  • sceneWillResignActive(_:) 함수는 scene과 상호작용을 중지할 때 호출됩니다. (예로 다른 scene으로 전환할 때)
  • sceneWillEnterForeground(_:) 함수는 scene이 foreground에 들어갈 때 호출됩니다. (예로 앱이 시작하거나 background에서 다시 시작될 때)
  • sceneDidEnterBackground(_:) 함수는 scene이 background에 들어갈때 때 호출됩니다. (예로 홈버튼을 눌러 앱을 최소화시킨 경우. 이때에는 background에서 아직 살아있습니다.)

위 함수들의 대칭성이 보이나요? 예를 들어 active/inactive, background/foreground, 그리고 connect/disconnect 등에서 말이죠. 이는 어떤 앱이나 프로세스의 라이프 사이클 이벤트 입니다.

2. AppDelegate: SceneSession

iOS 13부터 AppDelegate에는 scene sessions를 관리하는 두 가지 새로운 함수가 추가됐습니다. 앱에서 scene이 생성되면 scene session 객체가 관련된 scene의 모든 정보를 추적합니다.

이 함수는 다음과 같습니다.

  • application(_:configurationForConnecting:options:) 함수는 새 scene을 만들때 configuration 객체를 리턴해야 합니다.
  • application(_:didDiscardSceneSessions:) 함수는 사용자가 App Switcher를 통해 하나 이상의 scene을 닫을때 호출됩니다.

현재 scene session은 외부 디스플레이 또는 카플레이와 같은 scene에 대한 역할을 지정하는데 사용됩니다. 또한, scene의 상태를 복원하는데 사용되는데, 이를 통해 앱 시작간에 UI를 보존하고 재생성할 수 있습니다. 또한 scene session에 유저 정보를 할당할 수 있는데, 사실상 이것은 어떤것도 넣을 수 있는 dictionary 입니다.

application(_:didDiscardSceneSessions:) 함수는 매우 간단합니다. 사용자가 하나 이상의 scene을 닫을때 호출됩니다. 이 함수를 더 이상 필요하지 않은 scene의 리소스를 폐기할때 사용할 수 있습니다.

반면 SceneDelegatesceneDidDisconnect(_:) 함수는 scene이 disconnect 될 때 호출되지만, 반드시 폐기할 필요는 없습니다. 왜냐하면 이 시점에서는 다시 connect될 수 있기 때문입니다. 반면 application(_:didDiscardSceneSessions:)에서는 App Switcher를 사용하여 scene이 종료되는 순간을 나타냅니다.

3. Info.plist Application Scene Manifest

앱에서 지원하는 모든 scene은 Info.plist의 Application Scene Manifest에 선언되어야 합니다. 간단히 말해서 이 Application Scene Manifest에는 앱의 모든 scene이 나열되어 있어야 합니다. 대부분의 앱은 하나의 scene만 가지고 있을테지만, 푸시 알림을 위한 scene이나 특정 액션을 위한 scene을 추가적으로 만들 수도 있습니다.

Application Scene Manifest는 Info.plist 파일의 일부분인데, Info.plist는 앱 이름, 버전, 지원되는 세로/가로 모드 등의 configuration 값을 포함하고 있습니다.

중요한 것은 session 그 자체가 아니라 session type을 선언한다는 것 입니다. 앱은 하나의 scene과 이를 복사한 scene을 만드는 것을 지원하며, 이를 통해서 multi window 앱을 만들 수 있습니다.

아래는 Info.plist의 한 부분입니다.

최상위 레벨에서 Application Scene Manifest를 볼 수 있습니다. 그 안에는 Enable Multiple Windows라는 multi window 지원 여부를 설정하는 곳이 있으며, multi window를 지원하려면 값을 YES로 바꿔야 합니다. 그 아래에는 Application Session Role은 앱의 scene을 선언하는데 사용됩니다. 또 다른 아이템을 통해 외부 화면의 scene을 선언할 수도 있습니다.

가장 중요한 정보는 Application Session Role 배열에 저장됩니다. 이 배열은 다음과 같습니다.

  • Configuration Name은 유니크 해야합니다.
  • scene의 클래스 이름은 UIWndowScene 입니다.
  • scene을 위한 delegate의 이름은 일반적으로 SceneDelegate 입니다.
  • 이 scene의 UI가 포함된 Storyboard의 이름

Storyboard Name 항목은 아마도 Xcode 10 이하의 프로젝트 설정의 Main Interface 부분을 떠올리게 할 것입니다. 만약 scene을 사용하지 않는다면 여기서 메인 Storyboard를 설정할 수 있습니다.

그렇다면 SceneDelegateAppDelegate의 scene sessions을 이용해서 multi window 앱을 만드는 방법은 무엇입니까?

  • 먼저 우리는 SceneDelegate를 살펴봤습니다. 이것은 scene의 sceneDidBecomeActive(_:), sceneDidEnterBackground(_:)과 같이 라이프 사이클을 관리하고 반응합니다.
  • 그리고 우리는 AppDelegate의 새로운 함수를 확인했습니다.
  • 마지막으로 우리는 Info.plist의 Application Scene Manifest에 대해 알아봤습니다.

멋지군요. 이제부터 scene이 UI 구축에 어떻게 영향을 미치는지는 파트 2에서 알아봅시다!