스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
SwiftData로 앱 만들기
SwiftData로 앱 데이터를 영구적으로 유지하는 방법을 알아보세요. 저희와 함께 코드를 만들며 SwiftData를 다중 플랫폼 SwiftUI 앱에 적용해 보세요. 기존의 모델 클래스를 SwiftData 모델로 변환하고, 환경을 설정하고, 모델 레이어의 변경 사항을 UI에 반영하는 법을 알려드립니다. SwiftData 스토리지를 사용하면 문서 기반 응용 프로그램도 만들 수 있습니다. 이번 세션을 잘 이해하려면 SwiftData를 알고 계셔야 합니다. WWDC23의 'SwiftData 알아보기' 세션을 먼저 시청하세요.
챕터
- 0:00 - Meet the app
- 3:09 - SwiftData models
- 5:30 - Querying models to display in UI
- 9:43 - Creating and updating
- 12:30 - Document-based apps
리소스
관련 비디오
WWDC23
-
다운로드
안녕하세요 줄리아라고 합니다 SwiftUI 엔지니어죠 최근 저희는 SwiftData를 도입했습니다 Swift에서 모델 레이어를 영구적으로 유지하는 방법이죠 이번 세션에서는 SwiftUI 앱에서 SwiftData를 매끄럽게 통합하는 법을 알아보겠습니다 오늘 다룰 새로운 SwiftUI 기능은 SwiftData 모델과의 통합이 매끄럽게 이루어지게 하죠 'SwiftData 알아보기'를 아직 시청하지 않으셨다면 여기에서 기본 정보를 얻으시길 바랍니다 SwiftData와 SwiftUI가 어떻게 어우러지는지 보기 위해 플래시카드 앱을 만들어 볼게요 저는 전부터 위대한 발명의 날짜와 발명가를 기억하기 좋게 정리하는 도구를 만들고 싶었어요 SwiftData는 이런 작업에 아주 잘 맞습니다 플래시카드 덱을 영구적으로 유지해서 언제든 제가 열어보고 클릭해 볼 수 있게 하죠 SwiftData는 이 앱을 Mac과 iPhone, Watch, TV에서 사용할 수 있게 합니다 모든 플랫폼에서 쓸 수 있죠 코드 만들기를 시작할게요 이 세션에서 저는 여러분과 함께 앱을 제작할 거예요 지금 일시 정지를 누르고 컴패니언 Xcode 프로젝트를 다운로드하세요 시작점과 완성본이 준비되어 있는 아카이브입니다 시작 프로젝트를 열고 ContentView 파일로 가세요 이번 세션에서는 새로운 Xcode 기능을 활용하겠습니다 내장된 상호 작용형 Mac 라이브 프리뷰입니다 프리뷰 섹션에는 플래시카드 그리드가 있습니다 아무 카드나 클릭하면 뷰로 전환되고 카드를 하나씩 스크롤해서 볼 수 있죠 컴파일러를 발명한 사람이 누구인지 기억하세요? 카드를 클릭하면 뒤집히면서 답이 공개됩니다 이 앱에는 메모리에 저장된 샘플 카드가 들어 있는데 앱을 열고 새로 추가한 카드는 앱을 종료했을 때 사라집니다 이때 SwiftData가 필요한 거죠 SwiftData를 쓰면 플래시카드를 영구적으로 유지할 수 있어요 오늘은 SwiftData를 사용할 때 알아야 할 정보를 제가 준비한 할 일 목록을 통해 살펴보겠습니다 목록의 항목을 하나씩 함께 확인해 보시죠 저희가 만들 앱은 여러분도 방금 보셨습니다 이번에는 시작 프로젝트와 그 모델 클래스를 살펴보죠 이어서 SwiftData를 스토리지로 사용하기 위해 모델을 변환하고 수정하는 절차를 보여드리겠습니다 모델 클래스를 확장해 SwiftData 모델을 만드는 법과 모델 레이어의 변화에 맞춰 데이터를 쿼리하고 뷰를 업데이트하는 법 모델 생성하고 저장하는 법 UI 요소와 바인딩하는 법을 배울 겁니다 마지막으로는 SwiftData가 스토리지를 관리할 때 문서 기반 앱을 만드는 것이 얼마나 간단한지 보여드릴게요 시작 프로젝트에서 저는 시간을 아끼기 위해 플래시카드 한 장과 여러 뷰 그리고 지원 파일이 담긴 카드 모델을 만들었습니다 모든 카드는 앞뒷면의 텍스트와 생성 날짜를 저장합니다 아주 전형적인 모델이에요 SwiftData가 대신 저장하도록 업데이트해 보죠 먼저 SwiftData를 이 파일에 불러옵니다 이어서 해야 할 가장 큰 수정은 @Model 매크로를 정의에 추가하는 거예요 이제 이 클래스는 SwiftData로 영구 유지됩니다 더 입력할 게 없어요 끝입니다 또한 @Model 덕에 카드가 Observable 프로토콜에 적합성을 얻게 되니 이걸 ObservableObject 대신 사용할 수 있습니다 Observable 객체의 적합성과 @Published 프로퍼티 래퍼를 삭제하세요 전에는 ObservedObject 적합성을 사용해 CardEditorView 파일의 UI에서 카드를 직접 편집했었습니다 여기서 Observable을 도입하기 위해 'ObservedObject' 프로퍼티 래퍼를 'Bindable'로 교체합니다 이제 텍스트 필드가 카드의 앞면 텍스트와 직접 바인딩되죠 뒷면 텍스트도요 됐어요 새로운 Observable 매크로와 Bindable 프로퍼티 래퍼는 전보다 더 적은 코드로도 응용 프로그램의 데이터 흐름을 더 자연스럽게 만듭니다 뷰가 본문에서 Observable 유형 프로퍼티를 쓰면 해당 프로퍼티가 수정될 때 자동으로 업데이트되죠 이제 그 어느 때보다도 간단하게 모델의 가변 상태와 UI 요소를 바인딩할 수 있습니다 이와 관련해서 추천하고 싶은 WWDC23 세션은 'SwiftUI의 Observation 알아보기'입니다 Observable은 SwiftData가 있든 없든 데이터 흐름 코드를 놀라울 정도로 단순화하죠 모델에 대해서는 여기까지만 아시면 됩니다 더는 필요 없어요 정말 멋지죠? 이번에는 SwiftData에서 모델을 쿼리하고 UI에 띄우기 위해 ContentView로 바꾸겠습니다 SampleDeck.contents 대신 SwiftData가 가지고 있는 카드를 보여드릴 거예요
SwiftData 스토리지에 카드 어레이를 바인딩하려면 딱 한 가지 부분만 변경하면 됩니다 @State 프로퍼티 래퍼를 @Query로 바꾸는 겁니다 이게 끝이에요 프리뷰에서 볼 수 있듯이 지금은 띄울 카드가 없습니다 저장한 카드가 없기 때문이겠죠 UI에서 SwiftData가 관리하는 모델을 보고 싶으면 @Query를 사용하면 됩니다 @Query는 SwiftData에서 모델을 쿼리하는 프로퍼티 래퍼죠 모델에 일어나는 모든 변화를 뷰에 업데이트하기도 합니다 @State와 같은 기능입니다 뷰의 쿼리 프로퍼티 개수에는 제한이 없습니다 쿼리가 제공하는 가벼운 구문은 분류를 비롯해 주문과 필터링 변경 사항 애니메이팅까지 구성할 수 있게 하죠 쿼리는 뷰의 모델 콘텍스트를 데이터 소스로 사용합니다 @Query에는 모델 콘텍스트를 어떻게 제공해야 할까요? 모델 컨테이너에서 가져오면 됩니다 SwiftUI는 새로운 뷰와 씬 수정자를 개발해서 뷰의 ModelContainer를 편리하게 설정하고자 했죠 SwiftData를 사용하는 응용 프로그램에는 하나 이상의 ModelContainer가 필요한데요 이렇게 하면 @Query가 사용할 콘텍스트를 포함하는 스토리지 스택이 생성됩니다 뷰의 모델 컨테이너는 하나지만 응용 프로그램은 뷰 계층 구조에 따라 컨테이너를 여러 개 생성할 수 있죠 응용 프로그램이 modelContainer를 설정하지 않으면 윈도우와 뷰가 SwiftData를 통해 쿼리 모델을 저장할 수 없습니다 응용 프로그램에 필요한 건 보통 단일 모델 컨테이너죠 이 경우 전체 윈도우 그룹 씬을 설정하면 됩니다 윈도우와 뷰가 컨테이너를 상속받고 같은 그룹에서 생성된 다른 윈도우도 상속받습니다 모든 뷰는 단일 컨테이너에서 쓰기와 읽기를 합니다 일부 앱은 스토리지 스택 여러 개가 필요한데 각각의 윈도우에 모델 컨테이너 여러 개를 만들죠 SwiftUI는 뷰 수준에서 세분화 설정도 허용하는데요 같은 윈도우에서 다른 뷰는 개별 컨테이너를 갖기 때문에 한 컨테이너를 저장해도 다른 컨테이너에 영향은 없죠 쿼리에 데이터 소스를 제공하기 위해 modelContainer를 설정해 보겠습니다 앱 정의를 열어서
앱 윈도우의 모델 컨테이너를 설정하겠습니다 서브뷰는 뷰 수정자 목록에 나열된 모델 유형만을 생성하고, 읽고, 업데이트하고 삭제할 수 있습니다 이렇게 해서 설정이 끝났어요 여기서 한 걸음만 더 나아가죠 프리뷰에 샘플 데이터를 제공하는 거예요 앱에서 저는 인메모리 컨테이너에 샘플 카드를 정의했습니다 'PreviewSampleData' 파일을 열어서 타깃에 포함하죠 이 파일은 컨테이너의 정의와 샘플 데이터를 포함하고 있습니다 이것을 ContentView에서 사용해 제 프리뷰에 샘플 카드를 넣죠
@Query에 데이터 소스를 넣자 프리뷰에서 카드가 보입니다 SwiftData 스택에서 프리뷰를 생성할 때는 이것만 설정하면 됩니다 이번에는 SwiftData가 제가 만든 새 카드와 기존 카드의 변경 사항을 추적하고 저장하는지 확인할게요 이때는 뷰의 모델 콘텍스트를 사용해야 합니다 모델 콘텍스트에 접근하기 위해 SwiftUI는 새 환경 변수를 제공합니다 모델 컨테이너와 마찬가지로 각 뷰의 콘텍스트는 하나지만 일반적인 응용 프로그램의 콘텍스트 개수는 무제한입니다 저희 앱에서 콘텍스트는 이미 구성되어 있습니다 이 환경 변수는 모델 컨테이너를 설정할 때 자동으로 채워졌습니다 다시 Xcode로 돌아가 보죠 modelContext에 접근해야 카드를 저장하고 업데이트할 수 있어요
새로 생성된 카드를 모델 콘텍스트에 삽입해 저장하고자 하는 모델을 SwiftData에 알립니다
그렇다면 모델을 삽입한 뒤에는 'modelContext.save()'를 호출해 콘텍스트를 저장해야 할 것 같죠 하지만 그럴 필요 없습니다 SwiftData는 모델 콘텍스트를 자동 저장 해서 편리하죠 UI 관련 이벤트와 사용자 입력이 자동 저장의 트리거입니다 저장 걱정을 하지 않아도 SwiftData가 대신 해주죠 변경 사항이 즉시 영구 유지 상태로 바뀌는지 직접 확인해야 하는 경우는 많지 않습니다 SwiftData 스토리지를 공유하거나 전송하기 전에는 확인이 필요하죠 이때는 'save()'를 호출하세요 이제 앱으로 카드를 저장하고 쿼리할 수 있으니 직접 만들어 보죠 앱을 실행하고 추가 버튼을 눌러 카드를 생성합니다 아까 보았던 컴파일러 카드를 추가하죠 이제 앱을 종료하고 다시 실행해도 새 카드가 남아 있는지 볼게요
여기 있네요 뷰의 모델 콘텍스트에 접근해 카드를 추가하는 법을 배우셨어요 다 됐습니다 새로운 윈도우를 열어 보죠 첫 번째와 같은 덱이 보이죠 당연합니다 둘이 같은 모델 컨테이너를 쓰고 같은 데이터에 접근하니까요 하지만 다른 윈도우에서 다른 플래시카드 덱을 열 수 있다면 더 편리하겠죠 각각의 덱을 별도의 문서로 취급하고 싶다는 의미입니다 그러면 이 문서를 친구들과 공유할 수 있죠 문서 기반 앱은 macOS와 iOS, iPadOS에서 사용됩니다 사용자가 다양한 문서를 생성하고, 열고, 보고 편집할 수 있게 하는 응용 프로그램을 말하죠 모든 문서는 파일이고 사용자는 파일을 저장, 복사 공유할 수 있습니다 여러분께 알려드릴 반가운 소식이 있어요 SwiftUI는 SwiftData 기반 문서 앱을 지원한답니다 이렇게 접근해 볼게요 먼저 FlashCardApp 파일을 엽니다 문서 기반 앱은 iOS와 macOS에 있죠 이 플랫폼에서는 DocumentGroup 이니셜라이저로 바꿉니다
모델 유형 Card.self를 입력하겠습니다 콘텐츠 유형과 뷰 빌더도요 여기서 잠시 두 번째 매개변수인 콘텐츠 유형을 좀 더 자세히 다뤄 볼게요 SwiftData 문서 기반 앱은 맞춤형 콘텐츠 유형을 선언해야 합니다 각 SwiftData 문서는 고유 모델 집합을 기반으로 하고 디스크에서도 고유 표현을 가지게 됩니다 문서 콘텍스트에서는 콘텐츠 유형을 JPEG와 같은 이진 파일 포맷으로 생각하시면 됩니다 또 다른 유형의 문서인 패키지는 디스크상에 고정 구조를 지닌 디렉토리입니다 Xcode 프로젝트가 그런 경우죠 예를 들어 모든 JPEG 이미지는 같은 이진 구조를 지닙니다 그렇지 않다면 사진 편집기가 이미지를 읽지 못하겠죠 마찬가지로 모든 Xcode 프로젝트에는 특정 디렉토리와 파일이 포함되어 있습니다 사용자가 덱을 열면 운영 체제가 앱을 덱 포맷, 파일 확장과 연동할 수 있어야 합니다 그래서 콘텐츠 유형을 선언해야 하는 것입니다 SwiftData 문서는 패키지입니다 SwiftData 모델의 일부 프로퍼티를 'externalStorage' 속성으로 마킹하면 외부에 저장된 모든 아이템이 문서 패키지의 일부가 됩니다 UTType+FlashCards 파일에서 저는 새로운 콘텐츠 유형의 정의를 내려서 코드에서 편리하게 사용할 수 있게 했어요 Info.plist에도 같은 정의를 입력하겠습니다
이제 운영 체제에 새 콘텐츠 유형을 선언할 거예요 여기서 파일 확장을 지정해 주어야 우리 앱에서 생성된 카드 덱을 다른 문서와 구분할 수 있어요 이 샘플 앱에는 'sampledeck'을 확장으로 사용할게요
플래시카드 덱이라는 간단한 설명도 추가합니다
이 식별자는 코드에 있는 것과 정확히 일치해야 해요
SwiftData 문서는 패키지이기 때문에 우리 유형의 적합성도 확인해야 합니다 그 대상은 com.apple.package죠 이제 우리가 선언한 콘텐츠 유형을 사용해 보죠 저는 앱 정의로 돌아가서 DocumentGroup에 콘텐츠 유형을 입력합니다 뷰 빌더가 똑같습니다
모델 컨테이너는 설정하지 않는다는 걸 기억하세요 문서 인프라가 각 문서에 맞춰 설정해 줍니다 응용 프로그램을 실행해 어떻게 나오는지 보죠 앱이 실행되면 열린 패널이 나옵니다 문서 기반 응용 프로그램의 기본 동작이죠 새로운 문서를 생성하고 카드를 추가해 볼게요 이제 이 문서의 도구 막대에는 저장되지 않은 변경 사항이 표시됩니다 제가 Command+S를 누르면 저장 메시지가 나타나죠 이 덱은 우리가 아까 입력한 Info.plist와 같은 파일 확장으로 저장됩니다 새로운 덱을 저장하면 데스크톱에 제 첫 번째 플래시카드 덱이 나타납니다 Command+N을 눌러서 새 덱을 생성하거나 Command+O로 덱을 열 수도 있습니다 이런 단축어를 비롯한 다양한 기능은 문서 기반 응용 프로그램에서 자동으로 쓸 수 있죠 오늘은 SwiftUI 앱에서 SwiftData를 사용하는 법을 배우셨습니다 새로운 @Model 매크로와 @Query 프로퍼티 래퍼도 다뤘죠 모델 콘텍스트를 위한 새로운 환경 변수와 SwiftData를 문서 스토리지로 사용하면 얼마나 간편한지도 소개했어요 시청해 주셔서 감사합니다 즐겁게 앱을 만들어 보세요 ♪ ♪
-
-
3:33 - Defining a SwiftData model
@Model final class Card { var front: String var back: String var creationDate: Date init(front: String, back: String, creationDate: Date = .now) { self.front = front self.back = back self.creationDate = creationDate } }
-
4:25 - Binding to a SwiftData model
@Bindable var card: Card
-
5:52 - Query models from SwiftData storage
@Query private var cards: [Card]
-
8:27 - Setting up a model container for the window group
WindowGroup { ContentView() } .modelContainer(for: Card.self)
-
9:24 - Providing a preview with sample data
#Preview { ContentView() .frame(minWidth: 500, minHeight: 500) .modelContainer(previewContainer) }
-
10:30 - Accessing the model context of the ContentView
@Environment(\.modelContext) private var modelContext
-
10:51 - Insert a new model in the context
let newCard = Card(front: "Sample Front", back: "Sample Back") modelContext.insert(object: newCard)
-
13:34 - Start document-based application setup
@main struct SwiftDataFlashCardSample: App { var body: some Scene { #if os(iOS) || os(macOS) DocumentGroup(editing: Card.self, contentType: <#UTType#>) { <#code#> } #else WindowGroup { ContentView() .modelContainer(for: Card.self) } #endif } }
-
16:51 - Finish document-based application setup
@main struct SwiftDataFlashCardSample: App { var body: some Scene { #if os(iOS) || os(macOS) DocumentGroup(editing: Card.self, contentType: .flashCards) { ContentView() } #else WindowGroup { ContentView() .modelContainer(for: Card.self) } #endif } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.