샘플앱: https://developer.apple.com/documentation/SwiftUI/Building-a-document-based-app-using-SwiftData
모델링
기존 ObservableObject 기반 모델
final class Card: ObservableObject [
@Published var front: String
@Published var back: String
var creationDate: Date
init(front: String, back: String, creationDate: Date = .now) { ... }
}
extension Card: Identifiable { }
extension Card: Hashable {
// ...
}
SwiftData에서 쓰기 위한 모델로 변경
@Model은 @Observable 매크로를 포함한다. 따라서 ObservableObject와 Published 프로퍼티 래퍼를 없앨 수 있다.
@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
}
}
뷰에서는 텍스트 입력을 위해서 binding이 필요하기 때문에 Bindable로 해준다.
@Bindable var card: Card
UI에 보여주기 위한 모델 쿼리
뷰에서 SwiftData를 import하고 @State를 @Query로 바꿔준다.
@Query private var cards: [Card]
@Query
ModelContainer 설정하기
WindowGroup {
ContentView()
}
.modelContainer(for: Card.self)
preview용 샘플 컨테이너 만들기
import SwiftUI
import SwiftData
@MainActor
let previewContainer: ModelContainer = {
do {
let container = try ModelContainer(
for: Card.self, ModelConfiguration(inMemory: true)
)
for card in SampleDeck.contents {
contents.mainContext.insert(object: card)
}
return container
} catch {
fatalError("failed to create container")
}
}()
// preview
#Preview {
ContentView()
.frame(minWidth: 500, minHeight: 500)
.modelContainer(previewContainer)
}
모델 생성 및 갱신
modelContext에 접근해야 한다.
@Environment(\\.modelContext) private var modelContext
새 모델 넣기
let newCard = Card(front: "Sample Front", back: "Sample Back")
modelContext.insert(object: newCard)
[Bonus] Document-based app
사용자가 직접 다양한 타입의 document를 만들고, 열고, 보고, 편집할 수 있게 해주는 애플리케이션
SwiftData-backed Document-based app
@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
}
}
contentType
document는 바이너리 형태일 수도 있고, 패키지 형태일 수도 있다.
두 경우 모두 고정된 구조로 이루어져야만 다른 곳에서도 읽을 수 있다.
사용자가 커스텀 데이터 구조를 읽기 위해서는 OS가 어떤 앱에서 이를 읽을 수 있을지 알아야 한다. 이를 알 수 있게 해주는 것이 ContentType이다.
그래서 각 SwiftData 모델은 개별적인 ContentType을 가져야 한다.
import UniformTypeIdentifiers
extension UTType {
static var flashCards = UTType(exportedAs: "com.example.flashCards")
}
contentType까지 명시하기
@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
}
}