SwiftData๋ก ์คํค๋ง ๋ชจ๋ธ๋งํ๊ธฐ WWDC ์์ ๋ฒ์ญ/์ ๋ฆฌ๋ณธ์ ๋๋ค.
- ๋ชฉ์ฐจ
- 00:00 Intro
- 01:41 Utilizing schema macros | ์คํค๋ง ๋งคํฌ๋ก๋ฅผ ์ต๋ํ ํ์ฉํ๋ ๋ฒ(@Attribute, @Relationship, @Transient)
- 05:30 Evolving schemas | ์ฑ์ด ๋ณ๊ฒฝ๋ ๋ ์คํค๋ง ๋ง์ด๊ทธ๋ ์ด์ ์ผ๋ก ์คํค๋ง๋ฅผ ๊ฐ์ ํ๋ ๋ฒ
- 08:56 Wrap-up
- doc
- ์ ์์ฒญ
- SwiftData ์์๋ณด๊ธฐ
- SwiftData๋ก ์ฑ ๋น๋ํ๊ธฐ
0. Intro
SwiftData๋ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง ๋ฐ ๊ด๋ฆฌ๋ฅผ ์ํ ํ๋ ์์ํฌ์ ๋๋ค.
SwiftUI์ฒ๋ผ ์ธ๋ถ ํ์ผ ํ์ ์์ด ์ค๋ก์ง ์ฝ๋์๋ง ์ง์คํ ์ ์์ผ๋ฉฐ, Swift์ ์๋ก์ด ๋งคํฌ๋ก ์์คํ ์ผ๋ก ์ํํ API ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
SampleTrips ์ฑ์ ์์๋ก ์ดํด๋ณผ๊น์? SampleTrips ์ฑ์์๋ ์ฌ์ฉ์๊ฐ ์ฌํ์ ๊ณํํ ์ ์์ต๋๋ค.
๊ฐ ์ฌํ์ ์ด๋ฆ๊ณผ ๋ชฉ์ ์ง ์์ ๋ฐ ์ข ๋ฃ ๋ ์ง์ ํจ๊ป ์์ฑ๋ฉ๋๋ค. ๋ํ ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ๊ณผ ์์์ ๋ํ ๊ด๊ณ๋ ํฌํจํ ์ ์์ต๋๋ค.
import SwiftUI
final class Trip {
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
์ด Trip ํด๋์ค์ SwiftData๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ๊ฐ๋จํฉ๋๋ค.
๐๐ป SwiftData๋ฅผ importํ๊ณ , @Model ๋งคํฌ๋ก๋ฅผ class์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
import SwiftUI
import SwiftData
@Model
final class Trip {
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
@Model ๋งคํฌ๋ก๋ Trip ํด๋์ค๋ฅผ PersistentModel๊ณผ ์ผ์น์ํค๊ณ , descriptive ์คํค๋ง๋ฅผ ์์ฑํฉ๋๋ค. ์ด์ ๋ชจ๋ธ์ ์ ์ํ๋ ์ฝ๋๊ฐ ์ฑ ์คํค๋ง์ ์ง์ค ๊ณต๊ธ์์ด ๋์์ต๋๋ค.
1. Utilizing schema macros
SwiftData ์คํค๋ง ๋งคํฌ๋ก๋ฅผ ํตํด ์ง์์ ์ธ ๊ฒฝํ์ ํ๋์ด ์ฑ์์ ์๋ฒฝํ ์๋ํ๋๋ก ๋ง์ถคํํ ์ ์์ต๋๋ค.
๊ธฐ์กด ์คํค๋ง์์๋ ๊ฐ ์ฌํ์ ๊ณ ์ ํ ์ด๋ฆ์ ๋ณด์ฅํ์ง ์์์ต๋๋ค. ๋ฐ๋ผ์ ์ด๋ฆ์ด ๊ฐ์ ์ฌํ๋ผ๋ฆฌ ์ถฉ๋ํ ์ ์์ต๋๋ค. ์ด๋ @Attribute ์คํค๋ง ๋งคํฌ๋ก๋ก unique ์ต์ ์ ์ฌ์ฉํด ํด๊ฒฐํ ์ ์์ต๋๋ค.
@Model
final class Trip {
@Attribute(.unique) var name: String // << ์ฌ๊ธฐ
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
์ด์ , SwiftData๊ฐ ์์ฑํ๋ ์ฌํ ๋ชจ๋ธ์ ์คํค๋ง๊ฐ ์๊ตฌ ๋ฐฑ์๋์ ์ ์ฅ๋๋ ๋ชจ๋ ์ฌํ์ ๊ณ ์ ํ ์ด๋ฆ์ ๋ณด์ฅํฉ๋๋ค.
๋ง์ฝ ํด๋น ์ด๋ฆ์ ์ฌํ์ด ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒฝ์ฐ ์๊ตฌ ๋ฐฑ์๋์์ ์ต๊ทผ ๊ฐ์ผ๋ก ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ฅผ upsert(์ ์ํธ)๋ผ๊ณ ํฉ๋๋ค. ์ ์ํธ๋ ์ธ์ํธ๋ก ์์ํฉ๋๋ค. ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ถฉ๋ํ ์ธ์ํธ๊ฐ ์ ๋ฐ์ดํธ๋ก ๋ณํด ๊ธฐ์กด ๋ฐ์ดํฐ์ ํ๋กํผํฐ๋ฅผ ์ ๋ฐ์ดํธํ๊ฒ ๋ฉ๋๋ค.
Unique constraints
๋ค๋ฅธ ํ๋กํผํฐ์๋ ๊ณ ์ ํ ์ ์ฝ์ ์ ์ฉํ ์ ์์ต๋๋ค. ์ซ์๋ ๋ฌธ์์ด, UUID์ ๊ฐ์ ๊ธฐ๋ณธ๊ฐ ์ ํ์ด๋ผ๋ฉด ๊ฐ๋ฅํฉ๋๋ค.
์คํค๋ง๋ฅผ ์ข ๋ ์์ ํด ๋ณผ๊น์? ์ด์ ์ ์ง์ ํ start_date์ end_date์์ ์ฑ๊ฐ์ ๋ฐ์ค์ ์์ ๊ณ ์ถ์ต๋๋ค. ๊ทผ๋ฐ ๋ณ์ ์ด๋ฆ์ ๋ฐ๊พธ๋ฉด ์์ฑ๋ ์คํค๋ง์ ์ ํ๋กํผํฐ๋ก ๊ฐ์ฃผ๋ ๊ฒ๋๋ค. SwiftData๊ฐ ์ ํ๋กํผํฐ๋ฅผ ์์ฑํ๋ ๊ฑด ์ํ์ง ์๊ณ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋๋ก ์ ์งํ๊ณ ์ถ์ต๋๋ค.
๐๐ป ์ด๋ฅผ ์ํด์๋ ๊ธฐ์กด ์ด๋ฆ์ ํ๋กํผํฐ ์ด๋ฆ์ผ๋ก ๋งคํํ๋ฉด ๋ฉ๋๋ค!
@Attribute๋ฅผ ์ฌ์ฉํ๊ณ , originalName: ๋งค๊ฐ๋ณ์๋ฅผ ์ง์ ํด์์.
๊ธฐ์กด ์ด๋ฆ์ ๋งคํํ๋ฉด ๋ฐ์ดํฐ ์์ค์ ์๋ฐฉํ ์ ์๊ณ , ์คํค๋ง ์
๋ฐ์ดํธ๊ฐ SampleTrips ์ฑ์ ๋ค์ ์ถ์์ ๊ฐ๋จํ ๋ง์ด๊ทธ๋ ์ด์
์ ๋ณด์ฅํ๊ฒ ๋ฉ๋๋ค.
@Attribute(originalName: "start_date") var startDate: Date
@Attribute(originalName: "end_date") var endDate: Date
์ด์ธ์๋ @Attribute ๋งคํฌ๋ก๋ ๋ค์ํ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ์ธ๋ถ์ ์ ์ฅํ๊ณ ๋ณํ ๊ฐ๋ฅ์ฑ์ ์ง์ํฉ๋๋ค.
- External data
- Transformable
- Spotlight integration
- Hash modifier
Relationships
์ด์ Relationships๋ฅผ ์ดํด๋ณผ๊น์?
์ ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ์ด๋ ์์๋ฅผ ์ฌํ์ ์ถ๊ฐํ๋ฉด SwiftData๊ฐ ์์์ ์ผ๋ก ๋ชจ๋ธ ๊ฐ์ ์ญ๊ด๊ณ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋์ ์ค์ ํด์ค๋๋ค. ์์์ ์ญ๊ด๊ณ์๋ annotation์ด ์ ํ ํ์ ์์ต๋๋ค.
์์์ ์ญ๊ด๊ณ๋ ๊ธฐ๋ณธ ์ญ์ ๊ท์น์ ์ฌ์ฉํ์ฌ ์ฌํ์ด ์ญ์ ๋ ๋ ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ๊ณผ ์์ ํ๋กํผํฐ๋ฅผ null๋ก ๋ง๋ญ๋๋ค. ํ์ง๋ง ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ๊ณผ ์์๊ฐ ์ฌํ๊ณผ ํจ๊ป ์ญ์ ๋๊ธธ ์ํฉ๋๋ค.
๐๐ป @Relationship ๋งคํฌ๋ก์ cascade ์ญ์ ๊ท์น์ผ๋ก ์ด๋ฅผ ์ฝ๊ฒ ์ํํ ์ ์์ต๋๋ค.
@Model
final class Trip {
@Attribute(.unique) var name: String
var destination: String
@Attribute(originalName: "start_date") var startDate: Date
@Attribute(originalName: "end_date") var endDate: Date
@Relationship(.cascade)
var bucketList: [BucketListItem]? = []
@Relationship(.cascade)
var livingAccommodation: LivingAccommodation?
}
์ด์ ์ฌํ์ ์ง์ฐ๋ฉด ํด๋น ๊ด๊ณ๋ค๋ ํจ๊ป ์ญ์ ๋ฉ๋๋ค.
@Relationship ๋งคํฌ๋ก์ ๊ธฐ๋ฅ
originalName ๋ชจ๋ํ์ด์ด ๋ฟ๋ง ์๋๋ผ to-many ๊ด๊ณ์์ ์ต์ ๋ฐ ์ต๋ ๊ฐ์๋ฅผ ์ง์ ํ ์๋ ์์ต๋๋ค.
- Tailor your @Relationship metadata
- originalName
- Specify toMany count constraints
- Hash modifier
์ด์ , ์ฌํ์ ๋ช ๋ฒ ์กฐํํ๋์ง ์ถ์ ํ๋ ๋ฐฉ๋ฒ์ ์ถ๊ฐํ๊ณ ์ถ์ต๋๋ค. ํ์ง๋ง SwiftData๊ฐ ์กฐํ ์๋ฅผ ๊ณ์ ์ธ๊ธฐ๋ฅผ ๋ฐ๋ผ์ง๋ ์์ต๋๋ค.
๐๐ป @Transient ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. @Transient๋ก ํ๋กํผํฐ๋ฅผ decorateํ๋ฉด ํด๋น ํ๋กํผํฐ๋ ์ง์๋์ง ์์ต๋๋ค.
@Transient
var tripViews: Int = 0
@Transient ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํด์ ๋ถํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ง์ํ์ง ์๊ฒ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ transient ํ๋กํผํฐ์ ๊ธฐ๋ณธ๊ฐ์ ๋ฐ๋์ ์ง์ ํด์ผ SwiftData์์ ๊ฐ์ ธ์ฌ ๋ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ์ด ํ ๋น๋ฉ๋๋ค. ์คํค๋ง ๋งคํฌ๋ก ํ์ฉ์ ๋ํ ์์ธํ ์ ๋ณด๋ SwiftData ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์ !
- Hide properties from SwifData with @Transient
- Specify which properties do not persist
- Provide a default value
2. Evolving schemas
SampleTrips ์ฑ์ ์ถ์ํ ๋๋ง๋ค ์ฑ์ด ์ ๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ผ ํฉ๋๋ค. ํ๋กํผํฐ ์ถ๊ฐ ๋ฐ ์ ๊ฑฐ ๋ฑ์ ์คํค๋ง ๋ณ๊ฒฝ์ด ์์ ๋๋ ๋ฐ์ดํฐ ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋ฐ์ํฉ๋๋ค. ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ณต์กํ ์ ์์ง๋ง SwiftData๋ฅผ ์ฐ๋ฉด ๊ฐ๋จํ ํด๊ฒฐํ ์ ์์ต๋๋ค.
- Encapsulate your models at a specific version with VersionedSchema
- Order your versions with SchemaMigrationPlan
- Define each migration stage
- Lightweight
- Custom
VersionedSchema์ SchemaMigrationPlan์ด ๋์์ค๋๋ค. SwiftData ๋ชจ๋ธ์ด ๋ณ๊ฒฝ๋ ์ฑ์ ์ ๋ฒ์ ์ ์ถ์ํ ๋๋ง๋ค VersionedSchema๋ฅผ ์ ์ํ์ธ์. ์ด๋ ์ด์ ์ ์ถ์๋ ์คํค๋ง๋ฅผ ์บก์ํํฉ๋๋ค. ๊ฐ๊ฐ์ ์คํค๋ง ๋ฒ์ ์ด VersionedSchema๋ก ์ ์๋์ด์ผ ๋ฒ์ ์ฌ์ด์ ์ด๋ค ๋ณํ๊ฐ ์์๋์ง SwiftData๊ฐ ์ ์ ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ VersionedSchema์ total ordering์ ์ฌ์ฉํด SchemaMigrationPlan์ ์์ฑํ๋ฉด SwiftData๊ฐ ํ์ํ ๋ง์ด๊ทธ๋ ์ด์ ์ ์์๋๋ก ์ํํฉ๋๋ค. ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ์ ์คํค๋ง๋ฅผ ์ ๋ ฌํ์ผ๋ฉด ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ด์ง๋ฅผ ์ ์ํ ์ ์๋์ต๋๋ค.
๋ ๊ฐ์ง ์ ํ์ ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ด์ง๊ฐ ์ ๊ณต๋ฉ๋๋ค.
ํ๋๋ ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ๋๋ค. ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ค์์ ์ฑ์ ์ถ์ํ๋ฉด์ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๋ง์ด๊ทธ๋ ์ด์ ํ ๋ ์ถ๊ฐ ์ฝ๋๊ฐ ํ์ํ์ง ์์ต๋๋ค. ๋ ์ง ํ๋กํผํฐ์ originalName์ ์ถ๊ฐํ๊ฑฐ๋ ๊ด๊ณ์์ ์ญ์ ๊ท์น์ ์ง์ ํ๋ ๋ฑ์ ๋ณ๊ฒฝ์ ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ ํฉํฉ๋๋ค.
ํ์ง๋ง ์ฌํ ์ด๋ฆ์ ๊ณ ์ ํ๊ฒ ์ง๋ ๊ฑด ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ ํฉํ์ง ์์ต๋๋ค. ์ด๋๋ ์ฌ์ฉ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ด์ง๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๋์ผ ๊ณ ์ ํ ์ด๋ฆ์ ์ง๊ธฐ ์ ์ ์ค๋ณต๋ ์ฌํ์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
์ฐ์ ์ฒซ ๋ฒ์งธ ์ถ์๋ ๊ธฐ์กด ์คํค๋ง๋ฅผ ๊ฐ์ ธ์์ VersionedSchema์ ์บก์ํํฉ๋๋ค. ์ด๋ฅผ SampleTripsSchemaV1๋ก ์ง์ ํ์ต๋๋ค. ๊ฐ VersionedSchema๋ ์ ์๋ ๋ชจ๋ธ ํด๋์ค๋ฅผ ๋์ดํฉ๋๋ค.
enum SampleTripsSchemaV1: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// Define the other models in this version...
}
๋ ๋ฒ์งธ ๋ฒ์ ์คํค๋ง์๋ ์ฌํ ์ด๋ฆ์ ๊ณ ์ ์ฑ ์ ์ฝ์ ์ถ๊ฐํ์ต๋๋ค. VersionedSchema๋ฅผ ํ๋ ๋ ์์ฑํด์ Trip ๋ชจ๋ธ ํด๋์ค์ ์ ์ฉํ ๋ณ๊ฒฝ ์ฌํญ์ ์บก์ํํฉ๋๋ค.
enum SampleTripsSchemaV2: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
@Attribute(.unique) var name: String // << ๋ณ๊ฒฝ
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// Define the other models in this version...
}
์ธ ๋ฒ์งธ ๋ฒ์ ์คํค๋ง์๋ ๋๊ฐ์ด ํด์ ์์ ๋ฐ ์ข ๋ฃ ๋ ์ง์ ์ ์ฉ๋ ์ด๋ฆ ๋ณ๊ฒฝ์ ์บก์ฒํฉ๋๋ค.
enum SampleTripsSchemaV3: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
@Attribute(.unique) var name: String
var destination: String
@Attribute(originalName: "start_date") var startDate: Date // << ๋ณ๊ฒฝ
@Attribute(originalName: "end_date") var endDate: Date // << ๋ณ๊ฒฝ
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// Define the other models in this version...
}
์ด์ VersionedSchema๊ฐ ๋ชจ๋ ์ค๋น๋์ผ๋ SchemaMigrationPlan์ ๊ตฌ์ฑํด์ ์ถ์๋ ๋ฒ์ ๊ฐ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฒ๋ฆฌํ๋ ๋ฒ์ ์์๋ด ์๋ค.
- ์ฑ ์คํค๋ง์ total ordering์ ์ ๊ณตํฉ๋๋ค.
- ๊ฐ ๋ง์ด๊ทธ๋ ์ด์
์ด ๊ฒฝ๋์ธ์ง ์ฌ์ฉ์ ์ ์์ธ์ง annotation์ ๋ฌ์์ผ ํฉ๋๋ค.
1) V1์์ V2๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฌ์ฉ์ ์ง์ ์คํ ์ด์ง๊ฐ ํ์ํด์ ๋ฐ์ดํฐ๊ฐ ๋ง์ด๊ทธ๋ ์ด์ ๋๊ธฐ ์ ์ ์์ ์ ์ํํด์ผ ํฉ๋๋ค.
2) willMigrate ํด๋ก์ ์์ ๋ง์ด๊ทธ๋ ์ด์ ์ด ์งํ๋๊ธฐ ์ ์ ์ค๋ณต๋๋ ์ฌํ์ ์ญ์ ํ ์ ์์ต๋๋ค. V1์์ V2๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋ฐ์ํ๋ ์์ ์ SwiftData๊ฐ ๊ฐ์งํด ์ด ํด๋ก์ ๋ฅผ ์คํํฉ๋๋ค.
3) V2์์ V3๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ธ, originalName์ ๋ง์ด๊ทธ๋ ์ด์ ์ ๊ฒฝ๋์ด๋ฏ๋ก ํด๋น ์คํ ์ด์ง๋ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
enum SampleTripsMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [SampleTripsSchemaV1.self, SampleTripsSchemaV2.self, SampleTripsSchemaV3.self] } static var stages: [MigrationStage] { [migrateV1toV2, migrateV2toV3] } static let migrateV1toV2 = MigrationStage.custom( fromVersion: SampleTripsSchemaV1.self, toVersion: SampleTripsSchemaV2.self, willMigrate: { context in let trips = try? context.fetch(FetchDescriptor<SampleTripsSchemaV1.Trip>()) // De-duplicate Trip instances here... try? context.save() }, didMigrate: nil ) static let migrateV2toV3 = MigrationStage.lightweight( fromVersion: SampleTripsSchemaV2.self, toVersion: SampleTripsSchemaV3.self ) }
- ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ์ ์ธ๋ถ ์ฌํญ๊น์ง ๋ชจ๋ ์ ์ํ์ผ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์คํํ ์ ์์ต๋๋ค. ํ์ฌ ์คํค๋ง์ ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ์ด ํฌํจ๋ ModelContainer๋ฅผ ์ค์ ํ๋ฉด ๋์ ๋๋ค. ์ด๋ค ๋ฒ์ ์์๋ ์ต์ ๋ฆด๋ฆฌ์ฆ๋ก ์ ๊ทธ๋ ์ด๋ํ ์ ์๊ณ ๋ฐ์ดํฐ๋ ํ์คํ ๋ณด์กด๋ ๊ฒ์ ๋๋ค.
struct TripsApp: App {
let container = ModelContainer(
for: Trip.self,
migrationPlan: SampleTripsMigrationPlan.self
)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}
3. Wrap-up
์คํค๋ง ๋งคํฌ๋ก๋ฅผ ํ์ฉํด ์คํค๋ง์ ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ์ธ์.
์ฑ์ด ๊ฐ์ ๋ ๋๋ง๋ค VersionedSchema๋ก ๊ฐ์ ์ ์ ์บก์ฒํด ์ด์ ๋ฒ์ ์์๋ ์ฑ์ ๋ง์ด๊ทธ๋ ์ด์ ํ ์ ์์ต๋๋ค.
- Convey metadata with schema macros
- Version schemas as your app evolves
'๐ Apple > WWDC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftData] 03. Dive deeper into SwiftData(SwiftData ์์ธํ ์ดํด๋ณด๊ธฐ) (7) | 2024.09.02 |
---|---|
[SwiftData] 01. Meet SwiftData (SwiftData ๋ง๋๋ณด๊ธฐ) (1) | 2024.08.14 |
SwiftData๋ก ์คํค๋ง ๋ชจ๋ธ๋งํ๊ธฐ WWDC ์์ ๋ฒ์ญ/์ ๋ฆฌ๋ณธ์ ๋๋ค.
- ๋ชฉ์ฐจ
- 00:00 Intro
- 01:41 Utilizing schema macros | ์คํค๋ง ๋งคํฌ๋ก๋ฅผ ์ต๋ํ ํ์ฉํ๋ ๋ฒ(@Attribute, @Relationship, @Transient)
- 05:30 Evolving schemas | ์ฑ์ด ๋ณ๊ฒฝ๋ ๋ ์คํค๋ง ๋ง์ด๊ทธ๋ ์ด์ ์ผ๋ก ์คํค๋ง๋ฅผ ๊ฐ์ ํ๋ ๋ฒ
- 08:56 Wrap-up
- doc
- ์ ์์ฒญ
- SwiftData ์์๋ณด๊ธฐ
- SwiftData๋ก ์ฑ ๋น๋ํ๊ธฐ
0. Intro
SwiftData๋ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง ๋ฐ ๊ด๋ฆฌ๋ฅผ ์ํ ํ๋ ์์ํฌ์ ๋๋ค.
SwiftUI์ฒ๋ผ ์ธ๋ถ ํ์ผ ํ์ ์์ด ์ค๋ก์ง ์ฝ๋์๋ง ์ง์คํ ์ ์์ผ๋ฉฐ, Swift์ ์๋ก์ด ๋งคํฌ๋ก ์์คํ ์ผ๋ก ์ํํ API ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
SampleTrips ์ฑ์ ์์๋ก ์ดํด๋ณผ๊น์? SampleTrips ์ฑ์์๋ ์ฌ์ฉ์๊ฐ ์ฌํ์ ๊ณํํ ์ ์์ต๋๋ค.
๊ฐ ์ฌํ์ ์ด๋ฆ๊ณผ ๋ชฉ์ ์ง ์์ ๋ฐ ์ข ๋ฃ ๋ ์ง์ ํจ๊ป ์์ฑ๋ฉ๋๋ค. ๋ํ ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ๊ณผ ์์์ ๋ํ ๊ด๊ณ๋ ํฌํจํ ์ ์์ต๋๋ค.
import SwiftUI
final class Trip {
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
์ด Trip ํด๋์ค์ SwiftData๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ๊ฐ๋จํฉ๋๋ค.
๐๐ป SwiftData๋ฅผ importํ๊ณ , @Model ๋งคํฌ๋ก๋ฅผ class์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
import SwiftUI
import SwiftData
@Model
final class Trip {
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
@Model ๋งคํฌ๋ก๋ Trip ํด๋์ค๋ฅผ PersistentModel๊ณผ ์ผ์น์ํค๊ณ , descriptive ์คํค๋ง๋ฅผ ์์ฑํฉ๋๋ค. ์ด์ ๋ชจ๋ธ์ ์ ์ํ๋ ์ฝ๋๊ฐ ์ฑ ์คํค๋ง์ ์ง์ค ๊ณต๊ธ์์ด ๋์์ต๋๋ค.
1. Utilizing schema macros
SwiftData ์คํค๋ง ๋งคํฌ๋ก๋ฅผ ํตํด ์ง์์ ์ธ ๊ฒฝํ์ ํ๋์ด ์ฑ์์ ์๋ฒฝํ ์๋ํ๋๋ก ๋ง์ถคํํ ์ ์์ต๋๋ค.
๊ธฐ์กด ์คํค๋ง์์๋ ๊ฐ ์ฌํ์ ๊ณ ์ ํ ์ด๋ฆ์ ๋ณด์ฅํ์ง ์์์ต๋๋ค. ๋ฐ๋ผ์ ์ด๋ฆ์ด ๊ฐ์ ์ฌํ๋ผ๋ฆฌ ์ถฉ๋ํ ์ ์์ต๋๋ค. ์ด๋ @Attribute ์คํค๋ง ๋งคํฌ๋ก๋ก unique ์ต์ ์ ์ฌ์ฉํด ํด๊ฒฐํ ์ ์์ต๋๋ค.
@Model
final class Trip {
@Attribute(.unique) var name: String // << ์ฌ๊ธฐ
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
์ด์ , SwiftData๊ฐ ์์ฑํ๋ ์ฌํ ๋ชจ๋ธ์ ์คํค๋ง๊ฐ ์๊ตฌ ๋ฐฑ์๋์ ์ ์ฅ๋๋ ๋ชจ๋ ์ฌํ์ ๊ณ ์ ํ ์ด๋ฆ์ ๋ณด์ฅํฉ๋๋ค.
๋ง์ฝ ํด๋น ์ด๋ฆ์ ์ฌํ์ด ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒฝ์ฐ ์๊ตฌ ๋ฐฑ์๋์์ ์ต๊ทผ ๊ฐ์ผ๋ก ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ฅผ upsert(์ ์ํธ)๋ผ๊ณ ํฉ๋๋ค. ์ ์ํธ๋ ์ธ์ํธ๋ก ์์ํฉ๋๋ค. ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ถฉ๋ํ ์ธ์ํธ๊ฐ ์ ๋ฐ์ดํธ๋ก ๋ณํด ๊ธฐ์กด ๋ฐ์ดํฐ์ ํ๋กํผํฐ๋ฅผ ์ ๋ฐ์ดํธํ๊ฒ ๋ฉ๋๋ค.
Unique constraints
๋ค๋ฅธ ํ๋กํผํฐ์๋ ๊ณ ์ ํ ์ ์ฝ์ ์ ์ฉํ ์ ์์ต๋๋ค. ์ซ์๋ ๋ฌธ์์ด, UUID์ ๊ฐ์ ๊ธฐ๋ณธ๊ฐ ์ ํ์ด๋ผ๋ฉด ๊ฐ๋ฅํฉ๋๋ค.
์คํค๋ง๋ฅผ ์ข ๋ ์์ ํด ๋ณผ๊น์? ์ด์ ์ ์ง์ ํ start_date์ end_date์์ ์ฑ๊ฐ์ ๋ฐ์ค์ ์์ ๊ณ ์ถ์ต๋๋ค. ๊ทผ๋ฐ ๋ณ์ ์ด๋ฆ์ ๋ฐ๊พธ๋ฉด ์์ฑ๋ ์คํค๋ง์ ์ ํ๋กํผํฐ๋ก ๊ฐ์ฃผ๋ ๊ฒ๋๋ค. SwiftData๊ฐ ์ ํ๋กํผํฐ๋ฅผ ์์ฑํ๋ ๊ฑด ์ํ์ง ์๊ณ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋๋ก ์ ์งํ๊ณ ์ถ์ต๋๋ค.
๐๐ป ์ด๋ฅผ ์ํด์๋ ๊ธฐ์กด ์ด๋ฆ์ ํ๋กํผํฐ ์ด๋ฆ์ผ๋ก ๋งคํํ๋ฉด ๋ฉ๋๋ค!
@Attribute๋ฅผ ์ฌ์ฉํ๊ณ , originalName: ๋งค๊ฐ๋ณ์๋ฅผ ์ง์ ํด์์.
๊ธฐ์กด ์ด๋ฆ์ ๋งคํํ๋ฉด ๋ฐ์ดํฐ ์์ค์ ์๋ฐฉํ ์ ์๊ณ , ์คํค๋ง ์
๋ฐ์ดํธ๊ฐ SampleTrips ์ฑ์ ๋ค์ ์ถ์์ ๊ฐ๋จํ ๋ง์ด๊ทธ๋ ์ด์
์ ๋ณด์ฅํ๊ฒ ๋ฉ๋๋ค.
@Attribute(originalName: "start_date") var startDate: Date
@Attribute(originalName: "end_date") var endDate: Date
์ด์ธ์๋ @Attribute ๋งคํฌ๋ก๋ ๋ค์ํ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ์ธ๋ถ์ ์ ์ฅํ๊ณ ๋ณํ ๊ฐ๋ฅ์ฑ์ ์ง์ํฉ๋๋ค.
- External data
- Transformable
- Spotlight integration
- Hash modifier
Relationships
์ด์ Relationships๋ฅผ ์ดํด๋ณผ๊น์?
์ ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ์ด๋ ์์๋ฅผ ์ฌํ์ ์ถ๊ฐํ๋ฉด SwiftData๊ฐ ์์์ ์ผ๋ก ๋ชจ๋ธ ๊ฐ์ ์ญ๊ด๊ณ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋์ ์ค์ ํด์ค๋๋ค. ์์์ ์ญ๊ด๊ณ์๋ annotation์ด ์ ํ ํ์ ์์ต๋๋ค.
์์์ ์ญ๊ด๊ณ๋ ๊ธฐ๋ณธ ์ญ์ ๊ท์น์ ์ฌ์ฉํ์ฌ ์ฌํ์ด ์ญ์ ๋ ๋ ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ๊ณผ ์์ ํ๋กํผํฐ๋ฅผ null๋ก ๋ง๋ญ๋๋ค. ํ์ง๋ง ๋ฒํท ๋ฆฌ์คํธ ํญ๋ชฉ๊ณผ ์์๊ฐ ์ฌํ๊ณผ ํจ๊ป ์ญ์ ๋๊ธธ ์ํฉ๋๋ค.
๐๐ป @Relationship ๋งคํฌ๋ก์ cascade ์ญ์ ๊ท์น์ผ๋ก ์ด๋ฅผ ์ฝ๊ฒ ์ํํ ์ ์์ต๋๋ค.
@Model
final class Trip {
@Attribute(.unique) var name: String
var destination: String
@Attribute(originalName: "start_date") var startDate: Date
@Attribute(originalName: "end_date") var endDate: Date
@Relationship(.cascade)
var bucketList: [BucketListItem]? = []
@Relationship(.cascade)
var livingAccommodation: LivingAccommodation?
}
์ด์ ์ฌํ์ ์ง์ฐ๋ฉด ํด๋น ๊ด๊ณ๋ค๋ ํจ๊ป ์ญ์ ๋ฉ๋๋ค.
@Relationship ๋งคํฌ๋ก์ ๊ธฐ๋ฅ
originalName ๋ชจ๋ํ์ด์ด ๋ฟ๋ง ์๋๋ผ to-many ๊ด๊ณ์์ ์ต์ ๋ฐ ์ต๋ ๊ฐ์๋ฅผ ์ง์ ํ ์๋ ์์ต๋๋ค.
- Tailor your @Relationship metadata
- originalName
- Specify toMany count constraints
- Hash modifier
์ด์ , ์ฌํ์ ๋ช ๋ฒ ์กฐํํ๋์ง ์ถ์ ํ๋ ๋ฐฉ๋ฒ์ ์ถ๊ฐํ๊ณ ์ถ์ต๋๋ค. ํ์ง๋ง SwiftData๊ฐ ์กฐํ ์๋ฅผ ๊ณ์ ์ธ๊ธฐ๋ฅผ ๋ฐ๋ผ์ง๋ ์์ต๋๋ค.
๐๐ป @Transient ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. @Transient๋ก ํ๋กํผํฐ๋ฅผ decorateํ๋ฉด ํด๋น ํ๋กํผํฐ๋ ์ง์๋์ง ์์ต๋๋ค.
@Transient
var tripViews: Int = 0
@Transient ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํด์ ๋ถํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ง์ํ์ง ์๊ฒ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ transient ํ๋กํผํฐ์ ๊ธฐ๋ณธ๊ฐ์ ๋ฐ๋์ ์ง์ ํด์ผ SwiftData์์ ๊ฐ์ ธ์ฌ ๋ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ์ด ํ ๋น๋ฉ๋๋ค. ์คํค๋ง ๋งคํฌ๋ก ํ์ฉ์ ๋ํ ์์ธํ ์ ๋ณด๋ SwiftData ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์ !
- Hide properties from SwifData with @Transient
- Specify which properties do not persist
- Provide a default value
2. Evolving schemas
SampleTrips ์ฑ์ ์ถ์ํ ๋๋ง๋ค ์ฑ์ด ์ ๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ผ ํฉ๋๋ค. ํ๋กํผํฐ ์ถ๊ฐ ๋ฐ ์ ๊ฑฐ ๋ฑ์ ์คํค๋ง ๋ณ๊ฒฝ์ด ์์ ๋๋ ๋ฐ์ดํฐ ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋ฐ์ํฉ๋๋ค. ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ณต์กํ ์ ์์ง๋ง SwiftData๋ฅผ ์ฐ๋ฉด ๊ฐ๋จํ ํด๊ฒฐํ ์ ์์ต๋๋ค.
- Encapsulate your models at a specific version with VersionedSchema
- Order your versions with SchemaMigrationPlan
- Define each migration stage
- Lightweight
- Custom
VersionedSchema์ SchemaMigrationPlan์ด ๋์์ค๋๋ค. SwiftData ๋ชจ๋ธ์ด ๋ณ๊ฒฝ๋ ์ฑ์ ์ ๋ฒ์ ์ ์ถ์ํ ๋๋ง๋ค VersionedSchema๋ฅผ ์ ์ํ์ธ์. ์ด๋ ์ด์ ์ ์ถ์๋ ์คํค๋ง๋ฅผ ์บก์ํํฉ๋๋ค. ๊ฐ๊ฐ์ ์คํค๋ง ๋ฒ์ ์ด VersionedSchema๋ก ์ ์๋์ด์ผ ๋ฒ์ ์ฌ์ด์ ์ด๋ค ๋ณํ๊ฐ ์์๋์ง SwiftData๊ฐ ์ ์ ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ VersionedSchema์ total ordering์ ์ฌ์ฉํด SchemaMigrationPlan์ ์์ฑํ๋ฉด SwiftData๊ฐ ํ์ํ ๋ง์ด๊ทธ๋ ์ด์ ์ ์์๋๋ก ์ํํฉ๋๋ค. ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ์ ์คํค๋ง๋ฅผ ์ ๋ ฌํ์ผ๋ฉด ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ด์ง๋ฅผ ์ ์ํ ์ ์๋์ต๋๋ค.
๋ ๊ฐ์ง ์ ํ์ ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ด์ง๊ฐ ์ ๊ณต๋ฉ๋๋ค.
ํ๋๋ ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ๋๋ค. ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ค์์ ์ฑ์ ์ถ์ํ๋ฉด์ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๋ง์ด๊ทธ๋ ์ด์ ํ ๋ ์ถ๊ฐ ์ฝ๋๊ฐ ํ์ํ์ง ์์ต๋๋ค. ๋ ์ง ํ๋กํผํฐ์ originalName์ ์ถ๊ฐํ๊ฑฐ๋ ๊ด๊ณ์์ ์ญ์ ๊ท์น์ ์ง์ ํ๋ ๋ฑ์ ๋ณ๊ฒฝ์ ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ ํฉํฉ๋๋ค.
ํ์ง๋ง ์ฌํ ์ด๋ฆ์ ๊ณ ์ ํ๊ฒ ์ง๋ ๊ฑด ๊ฒฝ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ ํฉํ์ง ์์ต๋๋ค. ์ด๋๋ ์ฌ์ฉ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ด์ง๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๋์ผ ๊ณ ์ ํ ์ด๋ฆ์ ์ง๊ธฐ ์ ์ ์ค๋ณต๋ ์ฌํ์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
์ฐ์ ์ฒซ ๋ฒ์งธ ์ถ์๋ ๊ธฐ์กด ์คํค๋ง๋ฅผ ๊ฐ์ ธ์์ VersionedSchema์ ์บก์ํํฉ๋๋ค. ์ด๋ฅผ SampleTripsSchemaV1๋ก ์ง์ ํ์ต๋๋ค. ๊ฐ VersionedSchema๋ ์ ์๋ ๋ชจ๋ธ ํด๋์ค๋ฅผ ๋์ดํฉ๋๋ค.
enum SampleTripsSchemaV1: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// Define the other models in this version...
}
๋ ๋ฒ์งธ ๋ฒ์ ์คํค๋ง์๋ ์ฌํ ์ด๋ฆ์ ๊ณ ์ ์ฑ ์ ์ฝ์ ์ถ๊ฐํ์ต๋๋ค. VersionedSchema๋ฅผ ํ๋ ๋ ์์ฑํด์ Trip ๋ชจ๋ธ ํด๋์ค์ ์ ์ฉํ ๋ณ๊ฒฝ ์ฌํญ์ ์บก์ํํฉ๋๋ค.
enum SampleTripsSchemaV2: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
@Attribute(.unique) var name: String // << ๋ณ๊ฒฝ
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// Define the other models in this version...
}
์ธ ๋ฒ์งธ ๋ฒ์ ์คํค๋ง์๋ ๋๊ฐ์ด ํด์ ์์ ๋ฐ ์ข ๋ฃ ๋ ์ง์ ์ ์ฉ๋ ์ด๋ฆ ๋ณ๊ฒฝ์ ์บก์ฒํฉ๋๋ค.
enum SampleTripsSchemaV3: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
@Attribute(.unique) var name: String
var destination: String
@Attribute(originalName: "start_date") var startDate: Date // << ๋ณ๊ฒฝ
@Attribute(originalName: "end_date") var endDate: Date // << ๋ณ๊ฒฝ
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// Define the other models in this version...
}
์ด์ VersionedSchema๊ฐ ๋ชจ๋ ์ค๋น๋์ผ๋ SchemaMigrationPlan์ ๊ตฌ์ฑํด์ ์ถ์๋ ๋ฒ์ ๊ฐ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฒ๋ฆฌํ๋ ๋ฒ์ ์์๋ด ์๋ค.
- ์ฑ ์คํค๋ง์ total ordering์ ์ ๊ณตํฉ๋๋ค.
- ๊ฐ ๋ง์ด๊ทธ๋ ์ด์
์ด ๊ฒฝ๋์ธ์ง ์ฌ์ฉ์ ์ ์์ธ์ง annotation์ ๋ฌ์์ผ ํฉ๋๋ค.
1) V1์์ V2๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฌ์ฉ์ ์ง์ ์คํ ์ด์ง๊ฐ ํ์ํด์ ๋ฐ์ดํฐ๊ฐ ๋ง์ด๊ทธ๋ ์ด์ ๋๊ธฐ ์ ์ ์์ ์ ์ํํด์ผ ํฉ๋๋ค.
2) willMigrate ํด๋ก์ ์์ ๋ง์ด๊ทธ๋ ์ด์ ์ด ์งํ๋๊ธฐ ์ ์ ์ค๋ณต๋๋ ์ฌํ์ ์ญ์ ํ ์ ์์ต๋๋ค. V1์์ V2๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋ฐ์ํ๋ ์์ ์ SwiftData๊ฐ ๊ฐ์งํด ์ด ํด๋ก์ ๋ฅผ ์คํํฉ๋๋ค.
3) V2์์ V3๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ธ, originalName์ ๋ง์ด๊ทธ๋ ์ด์ ์ ๊ฒฝ๋์ด๋ฏ๋ก ํด๋น ์คํ ์ด์ง๋ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
enum SampleTripsMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [SampleTripsSchemaV1.self, SampleTripsSchemaV2.self, SampleTripsSchemaV3.self] } static var stages: [MigrationStage] { [migrateV1toV2, migrateV2toV3] } static let migrateV1toV2 = MigrationStage.custom( fromVersion: SampleTripsSchemaV1.self, toVersion: SampleTripsSchemaV2.self, willMigrate: { context in let trips = try? context.fetch(FetchDescriptor<SampleTripsSchemaV1.Trip>()) // De-duplicate Trip instances here... try? context.save() }, didMigrate: nil ) static let migrateV2toV3 = MigrationStage.lightweight( fromVersion: SampleTripsSchemaV2.self, toVersion: SampleTripsSchemaV3.self ) }
- ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ์ ์ธ๋ถ ์ฌํญ๊น์ง ๋ชจ๋ ์ ์ํ์ผ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ์คํํ ์ ์์ต๋๋ค. ํ์ฌ ์คํค๋ง์ ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ์ด ํฌํจ๋ ModelContainer๋ฅผ ์ค์ ํ๋ฉด ๋์ ๋๋ค. ์ด๋ค ๋ฒ์ ์์๋ ์ต์ ๋ฆด๋ฆฌ์ฆ๋ก ์ ๊ทธ๋ ์ด๋ํ ์ ์๊ณ ๋ฐ์ดํฐ๋ ํ์คํ ๋ณด์กด๋ ๊ฒ์ ๋๋ค.
struct TripsApp: App {
let container = ModelContainer(
for: Trip.self,
migrationPlan: SampleTripsMigrationPlan.self
)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}
3. Wrap-up
์คํค๋ง ๋งคํฌ๋ก๋ฅผ ํ์ฉํด ์คํค๋ง์ ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ์ธ์.
์ฑ์ด ๊ฐ์ ๋ ๋๋ง๋ค VersionedSchema๋ก ๊ฐ์ ์ ์ ์บก์ฒํด ์ด์ ๋ฒ์ ์์๋ ์ฑ์ ๋ง์ด๊ทธ๋ ์ด์ ํ ์ ์์ต๋๋ค.
- Convey metadata with schema macros
- Version schemas as your app evolves
'๐ Apple > WWDC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftData] 03. Dive deeper into SwiftData(SwiftData ์์ธํ ์ดํด๋ณด๊ธฐ) (7) | 2024.09.02 |
---|---|
[SwiftData] 01. Meet SwiftData (SwiftData ๋ง๋๋ณด๊ธฐ) (1) | 2024.08.14 |