App Essential in SwiftUI ft.Scene, View

DAMIN KIM
daily-monster
Published in
11 min readApr 10, 2024

안녕하세요 수요괴물 damin 입니다!

요즘 visionOS를 조금씩 공부하다보니 하나의 Scene을 사용하는 iOS와 달리 여러개의 Scene을 사용할 일이 많아 그 개념에 대해 알아보기 위해 App Essential in SwiftUI WWDC 세션을 듣고 App, Scene, View에 대해 간단히 정리해보았습니다.

WWDC 내용만으로 부족한게 많아서 블로그와 공식문서 내용을 참고하여 추가하였습니다.

WWDC 20에서 Scenes, Apps 프레임워크를 추가하여 SwiftUI 만으로 모든 앱을 만들 수 있게 되었습니다.

Views, scenes, and apps

View

뷰는 우리 아는 것처럼 화면을 구성하는 모든 UI컴포넌트 이미지, 레이블 등을 의미합니다.

그러나 보고 있는 모든 UI 가 한 앱에 속한건 아닙니다. 위 사진 처럼 다른 app 이 한 스크린에 보여지는 경우에 다른 앱의 UI도 한 화면에 보여질 수 있죠.

Scene

App이 screen을 컨트롤 하는것이 아닌 플랫폼 이 screen에 뭘 보여줄지를 결정함
플랫폼 별로 distinct region(구분되는 구역)별로 앱을 보여줄 수 있음 swiftUI에서 이런 distinct regionScene 이라고함

한 App을 iOS 플랫폼에서는 스크린에 하나의 Scene만 보여주고 iPadOS에서는 사용자가 Split한 구역에 각각 다른 Scene을 보여줄 수 있습니다.
Window는 콘텐츠를 스크린에 보여주는 가장 흔한 방법이죠.

플랫폼 별로 Window 개수가 달라지는데요 iOS, watchOS, tvOS 같은 경우 하나의 window를 보여주지만 iPadOS는 splitView로 여러개의 Window를 보여줄 수 있고
macOS 는 하나의 app을 여러개의 Window들을 통해 보여줄 수 있고
하나의 윈도우에 탭을 이용해서 여러개의 Scene을 개별적으로 보여줄 수 있습니다.

이 하나의 여러개의 Window탭을 가지는 Shared Window 는 자신만의 Scene을 가지고 이 complex scene안에 각 탭은 child scene이 되는 것 입니다. 여러개의 자식 child Scene이 탭으로 Complex Scene을 이루고 있습니다. 이런 Scenes들의 collection이 App을 구성합니다.

여러개의 View가 모여서 Scene 이 되고, 플랫폼 별로 독립적으로 보여질 수 있게 해주고 각 Scene 마다 독립적으로 View가 구성됩니다.

여러개의 Scene들이 모여 더 복잡한 Complex Scene을 구성 할 수 있고 (위의 탭이 있는 윈도우 처럼) 이러한 모든 Scene들이 App의 content로 구성됩니다.
(모든 Scene으로 이루어진다 하나의 App)

세션에 나온 예제에서 앱은 아래와 같이 구성되어 있습니다.
BookClub(App) — WindowGroup(Scene) — ReadingList(View)

body property

App 프로토콜, View 프로토콜 채택한 구조체 모두 body 연산 프로퍼티를 구현해줌
App, Scene, View 구조체 구현에 대해 자세히 확인해봅시다.

App, Scene, View

App, Scene, View에 대해서는 위 블로그 글을 내용을 바탕으로 작성하였습니다!

1. App

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
Text("Hello, world!")
}
}
}

@main attribute는 앱의 시작점 (entry point)를 의미하고 App 프로토콜을 앱을 실행할 수 있는 main()메서드 구현을 제공하지만 전체 앱 파일 중 1개의 entry point만 있어야 합니다. 즉 @main을 쓰거나 main() 메서드를 직접 실행하거나 혹은

@main 어트리뷰트를 안쓰고 빌드하려면 main파일을 만들고 App 구조체의 타입 메서드인 main을 직접 호출해줘야합니다.
뭐 굳이 이렇게 만들일은 없겠지만 @main이 이것들을 대신해준다고 생각하면 될듯합니다.

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
MyView()
}
#if os(macOS)
Setting {
SettingView()
}
#endif
}
}

다시 App 구조체 선언에서 body는 Scene 프로토콜을 채택한 객체들로 구성을 할 수 있으며 body에는 Setting, DocumentGroup 등과 같은 Scene을 사용해 하나 또는 여러개의 Scene들로 조합하여 구성하여 리턴합니다.

2. Scene

struct MyCustomScene: Scene {
var body: some Scene {
WindowGroup {
MyView()
}
WindowGroup {
MyView2()
}
}
}

각 Scene은 View hierarchy에서 루트뷰를 포함하고 있습니다. 시스템이 이 Scene의 LifeCycle을 관리합니다. (라이프사이클에 대해서는 ScenePhase 참고)

Scene은뷰 계층의 root element로써 동작합니다. 루트 View들의 루트가 Scene 입니다.
App에 들어가는 Scene을 SwiftUI에서 기본적으로 제공하는 WindowGroup 같은 Scene 말고 위 처럼 Scene 프로토콜을 채택하는 커스텀 Scene을 구현해줄 수도 있습니다. 이때 body에는 App에서처럼 하나 또는 여러개의 Scene들을 조합하여 구성할 수 있습니다.

3. View

struct MyView: View {
var body: some View {
Text("Hello, World!")
}
}

View는 Scene에 들어가는 뷰 계층의 기초 body 프로퍼티는 View를 리턴합니다.

App, Scene과 달리 하나의 View 구조체 body에서는 하나의 View만 리턴해야 합니다.
라고 해서 직접 해보니 body에 여러개의 View를 넣어도 문제가 없었습니다. 이는 아마도 자체적으로 하나의 View로 만들어 주는것으로 보입니다.

struct ContentView: View {
var body: some View {
// 2개의 뷰를 따로 감싸지 않고 반환해도 문제가 없었음
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")

}
}

Understanding scenes

Window group

WindowGroup을 통해 BookClub이라는 하나의 App을 여러개의 Window로 각각 다른 scene을 볼 수 있습니다.

iOS 같이 하나의 Window 를 갖는 경우 WindowGroup에서 하나의 Scene만 갖지만ipad나 macOS같이 여러개의 Scene을 갖는 경우 여러개의 Scene을 가지면서 각 Scene에서 독립적인 State를 가집니다. 즉, 각각의 Scene의 State가 다른 Scene의 State에 영향을 주지 않습니다!

그러나 새로운 창을 열거나 하는 등의 유저 인터랙션을 통해 WindowGroup이 새로운 Scene을 가지므로 이런 유저 인터랙션은 공유되고 있습니다. (그냥 유저 인터랙션은 하나의 App에서 관리되고 있다고 생각하면 될듯 합니다) 또한 각 Scene의 state 는 독립적, 유저 인터랙션은 공유됩니다.

SceneStorage

플랫폼이 Scene의 lifecycle을 관리합니다. 그래서 view state를 관리할 수 있도록 @SceneStorage라는 property wrapper를 소개하였습니다.

세션에서 소개된 SceneStorage에 대해서 잘 정리한 블로그 글이 있어서 읽어보시길 추천 드립니다!

Customizing apps

WindowGroup말고 다른 Scene 타입인 DocumentGroup이 있는데 자세한건 아래 세션 참고하시면 됩니다.
Build Document-Based Apps in SwiftUI — WWDC20

그 외 Setting Scene (macOS 용)나 Command modifier를 소개하였습니다. command 통해 Mac앱 상단 menu 바에 보이는 command를 지정할 수 있습니다.

세션의 내용중 여기서 의문이든 것은 commands API는 iOS도 지원한다고 되어있는데 iOS에서 command가 안쓰이는거 아닌가? 하는 점이였습니다. (본 세션에 굳이 필요없는 내용인데 새로 나와서 소개하는 것 같은 느낌..?)

Scene 관련 추가 내용

공식문서에는 A part of an app’s user interface with a life cycle managed by the system = 시스템의 의해 라이프 싸이클이 관리되는 앱의 유저 인터페이스의 한 부분
이라고 하는데 시스템에 의해 라이프 싸이클이 관리되고, App 과 View의 중간 단계에서 유저 인터랙션을 관리하는 개념이라고 생각하면 될것 같습니다.

Scene의 종류

우리가 메인으로 쓰는건 WindowGroup Scene이고 macOS app에서 setting 페이지는 Setting Scene으로 만들고 DocumentGroup씬은 도큐먼트 만들때 쓰는 Scene, visionOS의 Immersive Space등 여러가지 Scene이 있습니다.
(Window, WindowGroup과 ImmersiveSpace 외에든 안 써봐서 잘 모르겠네요..ㅠ)

본 세션을 보고 나니 App, Scene, View에 대한 개념이 조금 정리가 되는 것 같습니다.
그래도 좀 더 애플에서 정의한 Scene의 대해서 좀 더 이해하기 위해 다른 Scene들도 기회가 생기면 써봐야겠네요ㅎㅎ

--

--