WWDC 영상 바로가기 : Meet SwiftData
안녕하세요 비비입니다!
WWDC23 영상을 탐방하다가 SwiftData에 관심이 생겨 보던 중
보기만하면 기억에 남지 않을 것 같아서 기록을 시작해봅니다.
개인적으로는 JAVA의 SpringBoot 프레임워크가 생각나서
뭔가 CoreData 보다 친숙한 느낌이라 얼른 사용해보고싶네요!
정리한 내용 중 오역이 있다면 언제든지 말씀해주세요 😄
데이터 모델링 및 관리를 위한 프레임워크입니다.
SwiftUI와 마찬가지로 외부 파일 형식이 없는 코드에 전적으로 집중하고,
원활한 API 경험을 위해 새로운 Swift 매크로가 제공하는 표현력에 의존합니다.
또한 SwiftUI와 자연스럽게 통합되며 CloudKit
및 Widgets
와 같은 다른 플랫폼 기능과 함께 작동합니다.
@Model
매크로 사용하기
import SwiftData
@Model
class Trip {
var name: String
var destination: String
var endDate: Date
var startDate: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
*스키마(Schema): Database의 구조와 제약조건에 관해 전반적인 명세를 기술한 것. 즉 어떤 속성(Attribute)들로 이루어진 개체(Entity)들과, 그 개체들끼리의 관계(Relation)에 대한 정의 및 제약조건들을 기술한 것 입니다.
🌱 Attributes inferred from properties
속성으로 즉시 사용할 수 있도록 값 유형 속성을 조정합니다.
🌱 Support for basic value types
String, Int 및 Float와 같은 기본 값 유형이 포함됩니다.
🌱 Complex value types
컬렉션을 포함하여 구조체, 열거형 및 코딩 가능한 유형과 같은 보다 복잡한 값 유형을 포함할 수 있습니다.
즉, @Model
매크로만으로 속성을 쉽게 정의할 수 있습니다.
🌱 Relationships are inferred from reference types
SwiftData 모델은 유형을 관계로 참조합니다.
관계 및 모델 유형 컬렉션을 사용하여 모델 유형 간에 연결할 수 있습니다.
🌱 @Model
은 저장된 모든 속성을 수정할 수 있습니다.
🌱 아래의 매크로를 이용해 속성과 관계를 정의합니다.
@Attribute
@Relationship
🌱 @Transient
로 속성을 제외할 수 있습니다.
👉🏻 이건 Model your schema with SwiftData 에서 자세히 봐야겠습니다!
@Model
class Trip {
@Attribute(.unique) var name: String // Unique 지정
var destination: String
var endDate: Date
var startDate: Date
@Relationship(.cascade) var bucketList: [BucketListItem]? = [] // Trip 객체 삭제시 같이 삭제되도록 설정
var livingAccommodation: LivingAccommodation?
}
데이터 바인딩에 사용되는 2가지 주요 개체 ModelContainer
및 ModelContext
를 살펴볼게요!
저장하려는 모델 유형 목록을 지정하기만 하면 모델 컨테이너를 만들 수 있습니다.
컨테이너를 설정하여 모델 컨텍스트로 데이터를 가져오고 저장할 준비를 할 수 있습니다.
🌱 Persistence Backend
모델 유형에 대한 영구 백엔드 제공
🌱 Customized with configurations & Provides schema migration options
configurations
속성으로 URL
, CloudKit
및 그룹 컨테이너 식별자
, 마이그레이션 옵션
등을 커스터마이징할 수 있습니다.
// Initialize with only a schema
let container = try ModelContainer([Trip.self, LivingAccommodation.self])
// Initialize with configurations
let container = try ModelContainer(
for: [Trip.self, LivingAccommodation.self],
configurations: ModelConfiguration(url: URL("path"))
)
🧐 이 부분에서는 Trip과 LivingAccommodation을 동시에 container에 담는 것 같은데
DeepDive하면서 어떻게 정의되는건지 한번 더 살펴봐야겠습니다.
🌱 SwiftUI에서 사용하는 방법
import SwiftUI
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(
for: [Trip.self, LivingAccommodation.self]
)
}
}
🌱 Tracking updates
🌱 Fetching model
🌱 Saving changes
🌱 Undoing changes
업데이트 추적, 데이터 가져오기, 변경 사항 저장하기, 변경 사항 실행 쉬소하기 기능의 인터페이스입니다.
import SwiftUI
struct ContextView : View {
@Environment(\.modelContext) private var context
}
import SwiftData
// 1. 일반적으로 모델 컨테이너를 생성한 후 뷰의 환경에서 modelContext를 가져오는 경우
let context = container.mainContext
// 뷰 계층 구조 외부에서 공유된 기본 액터 바인딩 컨텍스트를 제공하도록 모델 컨테이너에 요청하거나
// 2. 주어진 모델 컨테이너에 대한 새 컨텍스트를 간단히 인스턴스화 하는 경우
let context = ModelContext(container)
컨텍스트가 있으면 데이터를 가져올 준비가 된 것입니다.
🧐 라고 소개는 되어있지만, 정확하게 context
가 뭘 의미하는건지는 조금 더 알아봐야겠습니다. 왜냐하면 위에서 modelContainer
가 [Trip.self, LivingAccommodation.self]
으로 2개의 model
을 동시에 가지고 있는데 context
를 활용하여 데이터를 어떻게 가져온다는건지 이 코드만 보고 확신할 수는 없네요..!
🌱 새로운 Swift 기본 유형
🌱 SortDescriptor의 개선 사항
let today = Date()
let tripPredicate = #Predicate<Trip> {
$0.destination == "New York" && // 목적지 : 뉴욕
$0.name.contains("birthday") && // 키워드 : 생일
$0.startDate > today // 오늘 이후, 계획된 여행 탐색
}
메모리의 데이터를 필터링 하여 가져오기 위한 쿼리 조건
이라고 해야할까요
CoreData 에서 사용하던 NSPredicate의 iOS 17 버전이라고 할 수 있습니다.
let descriptor = FetchDescriptor<Trip>(predicate: tripPredicate)
let trips = try context.fetch(descriptor)
FetchDescriptor
는 SwiftData 쿼리를 맞춤화
하는 방법을 제공합니다.
위와같이 필터 조건을 지정한 후 FetchDescriptor
를 통해 가져올 수 있습니다.
🌱 Updated to support all Comparable types Swift native keypaths
기본 Swift 유형 및 키 경로를 지원하기 위해 모든 비교 유형을 지원하도록 업데이트 되었습니다.
let descriptor = FetchDescriptor<Trip>(
sortBy: SortDescriptor(\Trip.name),
predicate: tripPredicate
)
let trips = try context.fetch(descriptor)
SortDescriptor
를 사용하여 가져온 여행을 구성할 순서를 지정
할 수 있습니다.
Predicate
및 SortDescriptor
외에도 prefetch
할 관련 개체를 지정하고
결과 수를 제한하고 결과에서 저장되지 않은 변경 사항을 제외하는 등의 작업을 수행할 수 있습니다.
🌱 Relationships to prefetch
🌱 Result limits
🌱 Exclude unsaved changes
ModelContext를 사용하여 이러한 작업을 구동함으로써 데이터를 쉽게 생성, 삭제 및 변경할 수 있습니다.
🌱 Basic operations (CRUD)
var myTrip = Trip(name: "Birthday Trip", destination: "New York")
// Insert a new trip
context.insert(myTrip)
// Delete an existing trip
context.delete(myTrip)
// Manually save changes to the context
try context.save()
@Model
매크로는 ModelContext가 변경 사항을 자동으로 추적하고 다음 저장 작업에 포함하도록 돕는다고 합니다.
데이터 저장소를 구성하고, 옵션을 변경하고, 실행 취소를 활성화하고, 자동 저장을 전환할 수 있습니다.
🌱 Leverage scene and view modifiers (뭐라고 해석하면 좋을지 혹시 아시는 분 계실까요 ?)
🌱 .modelContainer
를 사용하여 데이터 저장소 구성
🌱 SwiftUI environment로 등록하여 사용하기
설정한 후 데이터 사용을 시작하는 가장 쉬운 방법은 새로운 @Query
속성 래퍼입니다.
한 줄의 코드로 데이터베이스에 저장된 모든 항목을 쉽게 로드하고 필터링할 수 있습니다.
import SwiftUI
struct ContentView: View {
@Query(sort: \.startDate, order: .reverse) var trips: [Trip]
@Environment(\.modelContext) var modelContext
var body: some View {
NavigationStack() {
List {
ForEach(trips) { trip in
// ...
}
}
}
}
}
Meet SwiftData는 소개하는 내용이 많아 구체적인 코드를 살펴보기엔 짧은 감이 있네요 !
다음은 Build an app with SwiftData를 보고 조금 더 DeepDive 해보겠습니다!
부족한 글이지만 읽어주셔서 감사합니다 😊