-
0. Intro
-
1. Using the model macro
-
@Model
-
Attributes
-
Relationships
-
Additional metadata
-
2. Working with your data
-
ModelContainer
-
ModelContext
-
Fetching your data
-
Predicate
-
SortDescriptor
-
More FetchDescriptor options
-
Modifying your data
-
Modifying your data
-
3. Use SwiftData with SwiftUI
-
View modifiers
-
Observing changes
-
4. Wrap-up

SwiftData ๋ง๋๋ณด๊ธฐ WWDC ์์ ๋ฒ์ญ/์ ๋ฆฌ๋ณธ์ ๋๋ค.
- ๋ชฉ์ฐจ
- 00:00 Intro
- 01:07 Using the model macro | Swift์ ์๋ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ชจ๋ธ๋งํ๋ ์๋ก์ด @Model ๋งคํฌ๋ก
- 03:17 Working with your data | SwiftData๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์์ ํ๋ ๋ฒ
- 07:02 Use SwiftData with SwiftUI | SwiftData์ ๋งค๋๋ฝ๊ฒ ์๋ํ๋ ๋ค๋ฅธ ํ๋ซํผ ํ๋ ์์ํฌ ์๊ฐ
- 08:10 Wrap-up
- doc
0. Intro
SwiftData๋ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง ๋ฐ ๊ด๋ฆฌ ํ๋ ์์ํฌ๋ก ์ต์ Swift ์ฑ์ ๋ ์ ๊ทธ๋ ์ด๋์์ผ ์ค๋๋ค.
SwiftUI์ ์์ฐ์ค๋ฝ๊ฒ ํตํฉ๋๊ธฐ ๋๋ฌธ์ CloudKit, Widget ๊ฐ์ ๊ธฐ๋ฅ๊ณผ ๊ฐ์ด ํ์ฉํ ์ ์์ต๋๋ค.
1. Using the model macro
@Model
- Powerful new Swift macro
- Define your schema with code
- Add SwiftData funcionality to model types
@Model์ Swift ์ฝ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง ์ ์๋ฅผ ๋๋ ์๋ก์ด Swift ๋งคํฌ๋ก์ ๋๋ค.
SwiftData ์คํค๋ง๋ ๋ณดํต์ Swift ์ฝ๋์ด๋ฉฐ, ํ์ํ ๊ฒฝ์ฐ ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํ๋กํผํฐ์ ์ฃผ์ํํ ์ ์์ต๋๋ค.
Trip ํด๋์ค์ @Model ๋งคํฌ๋ก๋ฅผ ์ถ๊ฐํด ์คํค๋ง๋ฅผ ์์ฑํด๋ณผ๊น์?
class Trip {
var name: String
var destination: String
var endData: Date
var startData: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
import SwiftData
@Model
class Trip {
var name: String
var destination: String
var endData: Date
var startData: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
SwiftData์ ๋ชจ๋ธ๋ค์ ์ฑ์ ์คํค๋ง์ ๋ํ Source of Truth์ด๋ฉฐ, ์ ์งํ๋ ๊ฒฝํ์ ์ค ์ ์๋๋ฐ์. ํด๋์ค์ ์ ์ฅ ํ๋กํผํฐ์ ๋ณํ๋ฅผ ์ฃผ์ด ์ ์ง๋๋ ํ๋กํผํฐ๋ก ๋ฐ๊ฟ๋๋ค.
Attributes
- Attributes inferred from properties
- Support for basic value types
- Complex value types
- Struct, Enum, Codable, Collections of value types
SwiftData๋ ์์ฐ์ ์ผ๋ก ๊ฐ ํ์ ํ๋กํผํฐ๊ฐ ๋ฐ๋ก ์์ฑ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค. ๋ฌธ์์ด, ์ ์ํ, ์ค์ํ ๋ฑ ๊ธฐ๋ณธ ๊ฐ ํ์ ์ด ํฌํจ๋ฉ๋๋ค. ๋ํ, ๊ตฌ์กฐ์ฒด, ์ด๊ฑฐํ๊ณผ ๊ฐ์ ๋ ๋ณต์กํ ๊ฐ ํ์ ๋ ํฌํจ๋๊ณ ์ปฌ๋ ์ ๋ฑ ๋ณํ ๊ฐ๋ฅํ ํ์ ๋ ํฌํจ๋ฉ๋๋ค.
Relationships
- Relationships are inferred from reference types
- Other model types
- Collections of model types
SwiftData๋ ์ฐธ์กฐํ ํ์ ์ ๊ด๊ณ๋ก ์ค๊ณํฉ๋๋ค.
๋ชจ๋ธ ํ์ ๊ณผ ๋ชจ๋ธ ํ์ ์ ๊ด๊ณ ๊ทธ๋ฆฌ๊ณ ์ปฌ๋ ์ ์ฌ์ด์ ๊ด๊ณ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
Additional metadata
- @Model์ ํ์ ์ ๋ํ ์ ์ฅ ํ๋กํผํฐ๋ฅผ ๋ชจ๋ ์์ ํฉ๋๋ค.
- ํ๋กํผํฐ์ ๋ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด ์คํค๋ง ๊ตฌ์ถ ๋ฐฉ์์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
- @Attribute์ผ๋ก๋ ์ ์ผํ ์ ์ฝ์กฐ๊ฑด ์ถ๊ฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
- @Relationship์ผ๋ก๋ ์ธ๋ฒ์ค ๋ฐฉ์์ ์ ํํ๊ณ , ์ญ์ ์ ํ ๊ท์น์ ์ง์ ํ ์ ์์ต๋๋ค.
- @Transient๋ก๋ ํน์ ํ๋กํผํฐ๋ฅผ ํฌํจํ์ง ๋ง๋ผ๊ณ SwiftData์ ๋ช ๋ นํ ์ ์์ต๋๋ค.
Trip ์์์์, ์ ์ฅ ํ๋กํผํฐ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ์ฌ SwiftData์ ์คํค๋ง ์์ฑ์ ์กฐ์ ํด๋ณผ๊น์?
import SwiftData
@Model
class Trip {
var name: String
var destination: String
var endData: Date
var startData: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
import SwiftData
@Model
class Trip {
@Attribute(.unique) var name: String // @Attribute(.unique)๋ก ํ๋กํผํฐ๊ฐ ์ ์ผํ๋๋ก ์ง์
var destination: String
var endData: Date
var startData: Date
@Relationship(.cascade) var bucketList: [BucketListItem]? = [] // @Relationship๋ก ๊ด๊ณ๋ฅผ ์ค์ ํ๊ณ , (.cascade)๋ก ๊ด๋ จ๋ ํญ๋ชฉ์ ๊ฐ์ด ์ญ์ ๋๋๋ก ์ค์
var livingAccommodation: LivingAccommodation?
}
SwiftData ๋ชจ๋ธ๋ง์ ๋ํด ์ข ๋ ์์๋ณด๋ ค๋ฉด 'Model your schema with SwiftData' ์ธ์ ์ ํ์ธํด ๋ณด์ธ์.
2. Working with your data
@Model ํ์ ์ผ๋ก ์์ ํ๊ณ ์ด์์ ์ํด ์ฌ์ฉํด์ผํ๋ ๋ ๊ฐ์ง ์ฃผ์ ๊ฐ์ฒด ModelContainer, ModelContext๋ฅผ ์์๋ณผ๊น์?
ModelContainer
- Persistence backend
- Customized with configurations
- Provides schema migration options
ModelContainer๋ ๋ชจ๋ธ ํ์ ์ ์ ์ง๋๋ ๋ฐฑ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค. ์คํค๋ง๋ฅผ ์ง์ ํ์ฌ, ๊ธฐ๋ณธ ์ค์ ์ ์ฌ์ฉํ๊ฑฐ๋ ๊ตฌ์ฑํ๊ฑฐ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ต์ ์ผ๋ก ์ปค์คํ ํ ์ ์์ต๋๋ค.
์ ์ฅํ๊ณ ํ ๋ชจ๋ธ ํ์ ๋ชฉ๋ก์ ์ง์ ํ๊ธฐ๋ง ํ๋ฉด ๋ชจ๋ธ ์ปจํ ์ด๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์ปจํ ์ด๋๊ฐ ์ค์ ๋๋ฉด ModelContext๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์ ์ฅํ ์ค๋น๊ฐ ๋๋ฉ๋๋ค.
1) ์ปจํ ์ด๋๋ฅผ ์ปค์คํ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ, configuration์ ์ฌ์ฉํด URL, CloudKit, ๊ทธ๋ฃน ์ปจํ ์ด๋ ์๋ณ์ ๋ง์ด๊ทธ๋ ์ด์ ์ต์ ์ ๋ฐ๊ฟ ์ ์์ต๋๋ค.
import SwiftData
// ๊ธฐ๋ณธ
let container = try ModelContainer([Trip.self, LigingAccommodation.self])
// ์ปค์คํ
let customContainer = try ModelContainer(for: [Trip.self, LigingAccommodation.self],
configurations: ModelConfiguration(url: URL("path")))
2) SwiftUI์์ ์ปจํ ์ด๋๋ฅผ ์ค์ ํด ๋ทฐ ํ๊ฒฝ์์ ๋ฐ๋ก ์์ฑํ ์๋ ์์ต๋๋ค.
import SwiftData
import SwiftUI
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for:
[Trip.self,
LivingAccommodation.self]
)
}
}
ModelContext
- Tracking updates
- Fetching models
- Saving changes
- Undoing changes
ModelContext๋ ๋ชจ๋ธ์ ๋ชจ๋ ๋ณ๊ฒฝ ์ฌํญ์ ๊ด์ฐฐํ๊ณ ๋ชจ๋ธ์ ์ ์ฉํ ์ ์๋ ๋ง์ ์์ ์ ์ ๊ณตํฉ๋๋ค. ModelContext๋ ์ ๋ฐ์ดํธ๋ฅผ ์ถ์ ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉฐ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ฅํ๊ฑฐ๋ ์คํ ์ทจ์๋ฅผ ํ๋ ์ธํฐํ์ด์ค์ ๋๋ค.
์ฌ์ฉ๋ฐฉ๋ฒ์ ์์๋ณผ๊น์ !?
- SwiftUI ๋ทฐ ์์์ ์ฌ์ฉํ๊ธฐ
SwiftUI์์๋ ModelContainer๋ฅผ ์์ฑํ ํ์ ํ์ ๋ทฐ ๊ณ์ธต์์ ModelContext๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
import SwiftData
import SwiftUI
struct ContextView: View {
@Environment(\\.modelContext) private var context
}
- ๋ทฐ๊ฐ ์๋ ๊ณณ์์ ์ฌ์ฉํ๊ธฐ
- ๋ฐฉ๋ฒ 1) ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ ๋ฐ์์๋ ๋ชจ๋ธ ์ปจํ ์ด๋์๊ฒ ๊ณตํต์ ๋ฉ์ธ ์กํฐ ๋ฐ์ด๋ ์ปจํ ์คํธ๋ฅผ ๋ฌ๋ผ๊ณ ์์ฒญํ ์ ์์ต๋๋ค.
- ๋ฐฉ๋ฒ 2) ๋๋, ์ฃผ์ด์ง ์ปจํ ์ด๋์ ๋ํ ์๋ก์ด ์ปจํ ์คํธ๋ฅผ ์ธ์คํด์คํํ ์๋ ์์ต๋๋ค.
import SwiftData
let context = container.mainContext // ๋ฐฉ๋ฒ 1
let context = ModelContext() // ๋ฐฉ๋ฒ 2
์ปจํ ์คํธ๋ฅผ ๊ฐ์ ธ์์ผ๋, ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ค๋น๊ฐ ์ ๋ง ๋ค ๋์์ต๋๋ค!!
Fetching your data
- New Swift native types
- Predicate
- PetchDescriptor
- Improvements to SortDescriptor
Predicate, FetchDescriptor์ ๊ฐ์ ์ Swift ๋ค์ดํฐ๋ธ ํ์ ๊ณผ SortDescriptor์ ๊ฐ์ ์์ SwiftData์ ์ฅ์ ์ ๋๋ฆด ์ ์์ต๋๋ค !
Predicate
- Fully type checked
- #Predicate construction instead of text parsing
- Autocompleted keypaths
iOS 17์ ์๋ก ๋ฑ์ฅํ๋ Predicate๋ Swift ๋ค์ดํฐ๋ธ ํ์ ๊ณผ ํจ๊ป ๋์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ๋ ฅํ๊ฒ ํ์ํ(๋ถ๋ฅ)๋ ๊ตฌ์กฐ๋ฅผ ์ํด Swift ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ์์ ํ ํ์ ๊ฒ์ฌ๊ฐ ์ด๋ฃจ์ด์ ธ NSPredicate๋ฅผ ๋์ฒดํ๋ฉฐ, Xcode๋ง ์๋ค๋ฉด ์๋์์ฑ ๊ธฐ๋ฅ์ผ๋ก ๊ตฌํํ๊ธฐ๋ ์ฝ์ต๋๋ค.
์ํ Trip ์ฑ์ ๋ํ ์์ ์ ๊ตฌ์ถ ์์๋ฅผ ์ดํด๋ด ์๋ค.
let tripPredicate = #Predicate<Trip> {
$0.destination == "New York" && // โ๋ด์'์ด ๋ชฉ์ ์ง์ธ ์ฌํ์ ์ง์
$0.name.contains("birthday") && // ์์ผ๊ณผ ์ฐ๊ด๋ ์ฌํ๋ค๋ก ์ฟผ๋ฆฌ ๋ฒ์ ์ค์ด๊ธฐ
$0.startDate > today // ํฅํ ๊ณํ๋ ์ฌํ์๋ง ๊ด์ฌ์ด ์๋ค๊ณ ์ง์
}
๊ฐ์ ธ์ค๊ณ ์ ํ๋ ์ฌํ์ด ๋ฌด์์ธ์ง ๊ฒฐ์ ํ๋ค๋ฉด ์๋ก์ด FetchDescriptor ํ์ ์ ์ฌ์ฉํ์ฌ ModelContext์ ํด๋น ์ฌํ์ ๊ฐ์ ธ์ค๋ผ๊ณ ๋ช ๋ นํ ์ ์์ต๋๋ค.
let descriptor = FetchDescriptor<Trip>(predicate: tripPredicate)
let trips = try context.fetch(descriptor)
SortDescriptor
- Updated to support all Comparable types
- Swift native keypaths
SortDescriptor๋ FetchDescriptor์ ํจ๊ป ์ผ๋ถ ์ ๋ฐ์ดํธ๋์ด, ๋ค์ดํฐ๋ธ Swift ํ์ ๊ณผ ํคํจ์ค๋ฅผ ์ง์ํฉ๋๋ค.
SortDescriptor๋ฅผ ํ์ฉํด ์ฌํ์ ์์๋ฅผ ์ง์ ํ๋ ๋ฐ์ ์ฌ์ฉํด๋ณผ๊น์?
let descriptor = FetchDescriptor<Trip>(
sortBy: SortDescriptor(\\Trip.name),
predicate: tripPredicate
)
let trips = try context.fetch(descriptor)
More FetchDescriptor options
"FetchDescriptor"๋ ์ฌ๋ฌ๋ถ์ SwiftData์ ๋ง์ถคํ ์ฟผ๋ฆฌ๋ฅผ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
- Relationships to prefetch
- Result limits
- Exclude unsaved changes
Predicate์ Sort ์ธ์๋ ๊ด๋ จ ๊ฐ์ฒด๋ฅผ ์ง์ ํด ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ ์ฅ๋์ง ์์ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ธํด ๊ฒฐ๊ณผ ์นด์ดํธ๋ฅผ ์ ํํ๋ ๋ฑ ๋ง์ ๊ฒ๋ค์ ํ ์ ์์ต๋๋ค.
Modifying your data
ModelContext๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ ์์ฑ, ์ญ์ , ์์ ์ ์์ฃผ ์ฝ๊ฒ ๋ง๋ค๊ธฐ๋ ํฉ๋๋ค.
- Basic operations
- Inserting
- Deleting
- Saving
- Changing
์๋ ์ฝ๋์ ๊ฐ์ด Swift ํด๋์ค์ ๊ฐ์ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ๋ง๋ ํ ์ปจํ ์คํธ์ ์ถ๊ฐํ์ฌ ๋ณํ ์ถ์ ๋ฐ ์ ์ง์ ๊ฐ์ SwiftData ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
var myTrip = Trip(name: "Birthday Trip", destination: "New York")
// ...
// ์ถ๊ฐ ๋ฐฉ๋ฒ
context.insert(myTrip)
// ์ญ์ ๋ฐฉ๋ฒ
context.delete(myTrip)
// ๋ณ๊ฒฝ์ฌํญ ์ ์ฅ ๋ฐฉ๋ฒ (ModelContext์ ๊ฐ์ ์ ์ฅํ๊ณ ์ง์ ์ปจํ
์ด๋์ ์ปค๋ฐํ๋ผ๊ณ ์์ฒญ)
try context.save()
Modifying your data
- @Model macro modifies setters for change tracking and observation
- Updates automatically by the ModelContext
๋ชจ๋ธ ๊ฐ์ฒด์ ๋ํ ํ๋กํผํฐ ๊ฐ์ ๋ฐ๊พธ๋ ๊ฒ์ ํ์์ ํ๋กํผํฐ setter๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋งํผ์ด๋ ์ฝ์ต๋๋ค.
@Model ๋งคํฌ๋ก๋ ์ ์ฅ๋ ํ๋กํผํฐ๋ฅผ ์์ ํด ModelContext๊ฐ ๋ณ๊ฒฝ ์ฌํญ์ ์๋ ์ถ์ ํ๊ณ ๋ค์ ์ ์ฅ Operation์ ํฌํจ์ํค๋ ๊ฒ์ ๋์ต๋๋ค.
SwiftData ์ปจํ ์ด๋์ ์ปจํ ์คํธ, ์์ ์ ๋ํด์๋ โDive Deeper into SwiftDataโ์ธ์ ํ์ธ !
3. Use SwiftData with SwiftUI
- Seamless integration with SwiftUI
- Easy Configuration
- Automatically fetch data and update views
SwiftData๋ SwiftUI๋ฅผ ๊ณ ๋ คํ์ฌ ๋ง๋ค์ด์ก๊ธฐ์ ๋์ด ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ ์์ฃผ ์ฝ์ต๋๋ค. ๋ฐ๋ผ์ SwiftUI๋ SwiftData๋ฅผ ์ฌ์ฉํด๋ณผ ์ ์๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋๋ค.
SwiftData ์ปจํ ์ด๋๋ฅผ ์ค์ ํ๋ ๊ฒ์ด๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๋ทฐ ์ ๋ฐ์ดํธ๋ฅผ ํ๋ ๊ฒ์ด๋ ๊ด๋ จ๋ API๊ฐ ๊ตฌ์ถ๋์ด์๊ธฐ ๋๋ฌธ์, ์ SwiftUI scene, view modifiers๋ SwiftData ์ฑ ๊ตฌ์ถ์ ์์ํ ์ ์๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋๋ค.
View modifiers
- Leverage scene and view modifiers
- Configure data store with .modelContainer
- Propagated throughout SwiftUI environment
- SwiftUI will propagate your model context in its environment.
SwiftUI๋ฅผ ํตํด ๋ฐ์ดํฐ ์คํ ์ด ๊ตฌ์ฑ ์ต์ ๋ณ๊ฒฝ, ์คํ ์ทจ์ ์๋์ ์ฅ ํ ๊ธ์ด ๊ฐ๋ฅํ๋ฉฐ, SwiftUI๋ ํด๋น ํ๊ฒฝ์์ ๋ชจ๋ธ ์ปจํ ์คํธ๋ฅผ ์ ํํฉ๋๋ค.
SwiftUI ์ฝ๋๋ฅผ ํ์ธํด๋ณผ๊น์?
import SwiftData
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
// ...
}
}
}
}
}
์ผ๋จ ์ค์ ํ๊ณ ๋๋ฉด ๊ฐ์ฅ ์ฌ์ด ๋ฐ์ดํฐ ์ฌ์ฉ ๋ฐฉ์์ ์๋ก์ด @Query ํ๋กํผํฐ ๋ํผ์ ๋๋ค. ๋จ ํ ์ค์ ์ฝ๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ๋ฌด์์ด๋ ์ฝ๊ฒ ๋ก๋ฉํ๊ณ ํํฐ๋งํ ์ ์์ต๋๋ค.
Observing changes
- No need for @Published
- SwiftUI automatically refreshes
SwiftData๋ ๋ชจ๋ธ ํ๋กํผํฐ์ ๋ํด ์๋ก์ด observable ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
SwiftUI๋ observed ํ๋กํผํฐ์ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ์๋ก๊ณ ์นจํฉ๋๋ค.
SwiftUI์ SwiftData์ ํ๋ ์์ํฌ๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๋ฒ์ 'Build an app with SwiftData' ์ธ์ ์ ํ์ธํด ๋ณด์ธ์.
4. Wrap-up
- Define your schema using @Model
- Configure your ModelContainer
- Leverage SwiftUI and SwiftData
'๐ Apple > WWDC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftData] 03. Dive deeper into SwiftData(SwiftData ์์ธํ ์ดํด๋ณด๊ธฐ) (7) | 2024.09.02 |
---|---|
[SwiftData] 02. Model your schema with SwiftData(SwiftData๋ก ์คํค๋ง ๋ชจ๋ธ๋งํ๊ธฐ) (0) | 2024.08.20 |

SwiftData ๋ง๋๋ณด๊ธฐ WWDC ์์ ๋ฒ์ญ/์ ๋ฆฌ๋ณธ์ ๋๋ค.
- ๋ชฉ์ฐจ
- 00:00 Intro
- 01:07 Using the model macro | Swift์ ์๋ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ชจ๋ธ๋งํ๋ ์๋ก์ด @Model ๋งคํฌ๋ก
- 03:17 Working with your data | SwiftData๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์์ ํ๋ ๋ฒ
- 07:02 Use SwiftData with SwiftUI | SwiftData์ ๋งค๋๋ฝ๊ฒ ์๋ํ๋ ๋ค๋ฅธ ํ๋ซํผ ํ๋ ์์ํฌ ์๊ฐ
- 08:10 Wrap-up
- doc
0. Intro
SwiftData๋ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง ๋ฐ ๊ด๋ฆฌ ํ๋ ์์ํฌ๋ก ์ต์ Swift ์ฑ์ ๋ ์ ๊ทธ๋ ์ด๋์์ผ ์ค๋๋ค.
SwiftUI์ ์์ฐ์ค๋ฝ๊ฒ ํตํฉ๋๊ธฐ ๋๋ฌธ์ CloudKit, Widget ๊ฐ์ ๊ธฐ๋ฅ๊ณผ ๊ฐ์ด ํ์ฉํ ์ ์์ต๋๋ค.
1. Using the model macro
@Model
- Powerful new Swift macro
- Define your schema with code
- Add SwiftData funcionality to model types
@Model์ Swift ์ฝ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง ์ ์๋ฅผ ๋๋ ์๋ก์ด Swift ๋งคํฌ๋ก์ ๋๋ค.
SwiftData ์คํค๋ง๋ ๋ณดํต์ Swift ์ฝ๋์ด๋ฉฐ, ํ์ํ ๊ฒฝ์ฐ ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํ๋กํผํฐ์ ์ฃผ์ํํ ์ ์์ต๋๋ค.
Trip ํด๋์ค์ @Model ๋งคํฌ๋ก๋ฅผ ์ถ๊ฐํด ์คํค๋ง๋ฅผ ์์ฑํด๋ณผ๊น์?
class Trip {
var name: String
var destination: String
var endData: Date
var startData: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
import SwiftData
@Model
class Trip {
var name: String
var destination: String
var endData: Date
var startData: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
SwiftData์ ๋ชจ๋ธ๋ค์ ์ฑ์ ์คํค๋ง์ ๋ํ Source of Truth์ด๋ฉฐ, ์ ์งํ๋ ๊ฒฝํ์ ์ค ์ ์๋๋ฐ์. ํด๋์ค์ ์ ์ฅ ํ๋กํผํฐ์ ๋ณํ๋ฅผ ์ฃผ์ด ์ ์ง๋๋ ํ๋กํผํฐ๋ก ๋ฐ๊ฟ๋๋ค.
Attributes
- Attributes inferred from properties
- Support for basic value types
- Complex value types
- Struct, Enum, Codable, Collections of value types
SwiftData๋ ์์ฐ์ ์ผ๋ก ๊ฐ ํ์ ํ๋กํผํฐ๊ฐ ๋ฐ๋ก ์์ฑ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค. ๋ฌธ์์ด, ์ ์ํ, ์ค์ํ ๋ฑ ๊ธฐ๋ณธ ๊ฐ ํ์ ์ด ํฌํจ๋ฉ๋๋ค. ๋ํ, ๊ตฌ์กฐ์ฒด, ์ด๊ฑฐํ๊ณผ ๊ฐ์ ๋ ๋ณต์กํ ๊ฐ ํ์ ๋ ํฌํจ๋๊ณ ์ปฌ๋ ์ ๋ฑ ๋ณํ ๊ฐ๋ฅํ ํ์ ๋ ํฌํจ๋ฉ๋๋ค.
Relationships
- Relationships are inferred from reference types
- Other model types
- Collections of model types
SwiftData๋ ์ฐธ์กฐํ ํ์ ์ ๊ด๊ณ๋ก ์ค๊ณํฉ๋๋ค.
๋ชจ๋ธ ํ์ ๊ณผ ๋ชจ๋ธ ํ์ ์ ๊ด๊ณ ๊ทธ๋ฆฌ๊ณ ์ปฌ๋ ์ ์ฌ์ด์ ๊ด๊ณ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
Additional metadata
- @Model์ ํ์ ์ ๋ํ ์ ์ฅ ํ๋กํผํฐ๋ฅผ ๋ชจ๋ ์์ ํฉ๋๋ค.
- ํ๋กํผํฐ์ ๋ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด ์คํค๋ง ๊ตฌ์ถ ๋ฐฉ์์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
- @Attribute์ผ๋ก๋ ์ ์ผํ ์ ์ฝ์กฐ๊ฑด ์ถ๊ฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
- @Relationship์ผ๋ก๋ ์ธ๋ฒ์ค ๋ฐฉ์์ ์ ํํ๊ณ , ์ญ์ ์ ํ ๊ท์น์ ์ง์ ํ ์ ์์ต๋๋ค.
- @Transient๋ก๋ ํน์ ํ๋กํผํฐ๋ฅผ ํฌํจํ์ง ๋ง๋ผ๊ณ SwiftData์ ๋ช ๋ นํ ์ ์์ต๋๋ค.
Trip ์์์์, ์ ์ฅ ํ๋กํผํฐ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ์ฌ SwiftData์ ์คํค๋ง ์์ฑ์ ์กฐ์ ํด๋ณผ๊น์?
import SwiftData
@Model
class Trip {
var name: String
var destination: String
var endData: Date
var startData: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
import SwiftData
@Model
class Trip {
@Attribute(.unique) var name: String // @Attribute(.unique)๋ก ํ๋กํผํฐ๊ฐ ์ ์ผํ๋๋ก ์ง์
var destination: String
var endData: Date
var startData: Date
@Relationship(.cascade) var bucketList: [BucketListItem]? = [] // @Relationship๋ก ๊ด๊ณ๋ฅผ ์ค์ ํ๊ณ , (.cascade)๋ก ๊ด๋ จ๋ ํญ๋ชฉ์ ๊ฐ์ด ์ญ์ ๋๋๋ก ์ค์
var livingAccommodation: LivingAccommodation?
}
SwiftData ๋ชจ๋ธ๋ง์ ๋ํด ์ข ๋ ์์๋ณด๋ ค๋ฉด 'Model your schema with SwiftData' ์ธ์ ์ ํ์ธํด ๋ณด์ธ์.
2. Working with your data
@Model ํ์ ์ผ๋ก ์์ ํ๊ณ ์ด์์ ์ํด ์ฌ์ฉํด์ผํ๋ ๋ ๊ฐ์ง ์ฃผ์ ๊ฐ์ฒด ModelContainer, ModelContext๋ฅผ ์์๋ณผ๊น์?
ModelContainer
- Persistence backend
- Customized with configurations
- Provides schema migration options
ModelContainer๋ ๋ชจ๋ธ ํ์ ์ ์ ์ง๋๋ ๋ฐฑ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค. ์คํค๋ง๋ฅผ ์ง์ ํ์ฌ, ๊ธฐ๋ณธ ์ค์ ์ ์ฌ์ฉํ๊ฑฐ๋ ๊ตฌ์ฑํ๊ฑฐ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ต์ ์ผ๋ก ์ปค์คํ ํ ์ ์์ต๋๋ค.
์ ์ฅํ๊ณ ํ ๋ชจ๋ธ ํ์ ๋ชฉ๋ก์ ์ง์ ํ๊ธฐ๋ง ํ๋ฉด ๋ชจ๋ธ ์ปจํ ์ด๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์ปจํ ์ด๋๊ฐ ์ค์ ๋๋ฉด ModelContext๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์ ์ฅํ ์ค๋น๊ฐ ๋๋ฉ๋๋ค.
1) ์ปจํ ์ด๋๋ฅผ ์ปค์คํ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ, configuration์ ์ฌ์ฉํด URL, CloudKit, ๊ทธ๋ฃน ์ปจํ ์ด๋ ์๋ณ์ ๋ง์ด๊ทธ๋ ์ด์ ์ต์ ์ ๋ฐ๊ฟ ์ ์์ต๋๋ค.
import SwiftData
// ๊ธฐ๋ณธ
let container = try ModelContainer([Trip.self, LigingAccommodation.self])
// ์ปค์คํ
let customContainer = try ModelContainer(for: [Trip.self, LigingAccommodation.self],
configurations: ModelConfiguration(url: URL("path")))
2) SwiftUI์์ ์ปจํ ์ด๋๋ฅผ ์ค์ ํด ๋ทฐ ํ๊ฒฝ์์ ๋ฐ๋ก ์์ฑํ ์๋ ์์ต๋๋ค.
import SwiftData
import SwiftUI
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for:
[Trip.self,
LivingAccommodation.self]
)
}
}
ModelContext
- Tracking updates
- Fetching models
- Saving changes
- Undoing changes
ModelContext๋ ๋ชจ๋ธ์ ๋ชจ๋ ๋ณ๊ฒฝ ์ฌํญ์ ๊ด์ฐฐํ๊ณ ๋ชจ๋ธ์ ์ ์ฉํ ์ ์๋ ๋ง์ ์์ ์ ์ ๊ณตํฉ๋๋ค. ModelContext๋ ์ ๋ฐ์ดํธ๋ฅผ ์ถ์ ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉฐ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ฅํ๊ฑฐ๋ ์คํ ์ทจ์๋ฅผ ํ๋ ์ธํฐํ์ด์ค์ ๋๋ค.
์ฌ์ฉ๋ฐฉ๋ฒ์ ์์๋ณผ๊น์ !?
- SwiftUI ๋ทฐ ์์์ ์ฌ์ฉํ๊ธฐ
SwiftUI์์๋ ModelContainer๋ฅผ ์์ฑํ ํ์ ํ์ ๋ทฐ ๊ณ์ธต์์ ModelContext๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
import SwiftData
import SwiftUI
struct ContextView: View {
@Environment(\\.modelContext) private var context
}
- ๋ทฐ๊ฐ ์๋ ๊ณณ์์ ์ฌ์ฉํ๊ธฐ
- ๋ฐฉ๋ฒ 1) ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ ๋ฐ์์๋ ๋ชจ๋ธ ์ปจํ ์ด๋์๊ฒ ๊ณตํต์ ๋ฉ์ธ ์กํฐ ๋ฐ์ด๋ ์ปจํ ์คํธ๋ฅผ ๋ฌ๋ผ๊ณ ์์ฒญํ ์ ์์ต๋๋ค.
- ๋ฐฉ๋ฒ 2) ๋๋, ์ฃผ์ด์ง ์ปจํ ์ด๋์ ๋ํ ์๋ก์ด ์ปจํ ์คํธ๋ฅผ ์ธ์คํด์คํํ ์๋ ์์ต๋๋ค.
import SwiftData
let context = container.mainContext // ๋ฐฉ๋ฒ 1
let context = ModelContext() // ๋ฐฉ๋ฒ 2
์ปจํ ์คํธ๋ฅผ ๊ฐ์ ธ์์ผ๋, ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ค๋น๊ฐ ์ ๋ง ๋ค ๋์์ต๋๋ค!!
Fetching your data
- New Swift native types
- Predicate
- PetchDescriptor
- Improvements to SortDescriptor
Predicate, FetchDescriptor์ ๊ฐ์ ์ Swift ๋ค์ดํฐ๋ธ ํ์ ๊ณผ SortDescriptor์ ๊ฐ์ ์์ SwiftData์ ์ฅ์ ์ ๋๋ฆด ์ ์์ต๋๋ค !
Predicate
- Fully type checked
- #Predicate construction instead of text parsing
- Autocompleted keypaths
iOS 17์ ์๋ก ๋ฑ์ฅํ๋ Predicate๋ Swift ๋ค์ดํฐ๋ธ ํ์ ๊ณผ ํจ๊ป ๋์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ๋ ฅํ๊ฒ ํ์ํ(๋ถ๋ฅ)๋ ๊ตฌ์กฐ๋ฅผ ์ํด Swift ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ์์ ํ ํ์ ๊ฒ์ฌ๊ฐ ์ด๋ฃจ์ด์ ธ NSPredicate๋ฅผ ๋์ฒดํ๋ฉฐ, Xcode๋ง ์๋ค๋ฉด ์๋์์ฑ ๊ธฐ๋ฅ์ผ๋ก ๊ตฌํํ๊ธฐ๋ ์ฝ์ต๋๋ค.
์ํ Trip ์ฑ์ ๋ํ ์์ ์ ๊ตฌ์ถ ์์๋ฅผ ์ดํด๋ด ์๋ค.
let tripPredicate = #Predicate<Trip> {
$0.destination == "New York" && // โ๋ด์'์ด ๋ชฉ์ ์ง์ธ ์ฌํ์ ์ง์
$0.name.contains("birthday") && // ์์ผ๊ณผ ์ฐ๊ด๋ ์ฌํ๋ค๋ก ์ฟผ๋ฆฌ ๋ฒ์ ์ค์ด๊ธฐ
$0.startDate > today // ํฅํ ๊ณํ๋ ์ฌํ์๋ง ๊ด์ฌ์ด ์๋ค๊ณ ์ง์
}
๊ฐ์ ธ์ค๊ณ ์ ํ๋ ์ฌํ์ด ๋ฌด์์ธ์ง ๊ฒฐ์ ํ๋ค๋ฉด ์๋ก์ด FetchDescriptor ํ์ ์ ์ฌ์ฉํ์ฌ ModelContext์ ํด๋น ์ฌํ์ ๊ฐ์ ธ์ค๋ผ๊ณ ๋ช ๋ นํ ์ ์์ต๋๋ค.
let descriptor = FetchDescriptor<Trip>(predicate: tripPredicate)
let trips = try context.fetch(descriptor)
SortDescriptor
- Updated to support all Comparable types
- Swift native keypaths
SortDescriptor๋ FetchDescriptor์ ํจ๊ป ์ผ๋ถ ์ ๋ฐ์ดํธ๋์ด, ๋ค์ดํฐ๋ธ Swift ํ์ ๊ณผ ํคํจ์ค๋ฅผ ์ง์ํฉ๋๋ค.
SortDescriptor๋ฅผ ํ์ฉํด ์ฌํ์ ์์๋ฅผ ์ง์ ํ๋ ๋ฐ์ ์ฌ์ฉํด๋ณผ๊น์?
let descriptor = FetchDescriptor<Trip>(
sortBy: SortDescriptor(\\Trip.name),
predicate: tripPredicate
)
let trips = try context.fetch(descriptor)
More FetchDescriptor options
"FetchDescriptor"๋ ์ฌ๋ฌ๋ถ์ SwiftData์ ๋ง์ถคํ ์ฟผ๋ฆฌ๋ฅผ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
- Relationships to prefetch
- Result limits
- Exclude unsaved changes
Predicate์ Sort ์ธ์๋ ๊ด๋ จ ๊ฐ์ฒด๋ฅผ ์ง์ ํด ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ ์ฅ๋์ง ์์ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ธํด ๊ฒฐ๊ณผ ์นด์ดํธ๋ฅผ ์ ํํ๋ ๋ฑ ๋ง์ ๊ฒ๋ค์ ํ ์ ์์ต๋๋ค.
Modifying your data
ModelContext๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ ์์ฑ, ์ญ์ , ์์ ์ ์์ฃผ ์ฝ๊ฒ ๋ง๋ค๊ธฐ๋ ํฉ๋๋ค.
- Basic operations
- Inserting
- Deleting
- Saving
- Changing
์๋ ์ฝ๋์ ๊ฐ์ด Swift ํด๋์ค์ ๊ฐ์ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ๋ง๋ ํ ์ปจํ ์คํธ์ ์ถ๊ฐํ์ฌ ๋ณํ ์ถ์ ๋ฐ ์ ์ง์ ๊ฐ์ SwiftData ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
var myTrip = Trip(name: "Birthday Trip", destination: "New York")
// ...
// ์ถ๊ฐ ๋ฐฉ๋ฒ
context.insert(myTrip)
// ์ญ์ ๋ฐฉ๋ฒ
context.delete(myTrip)
// ๋ณ๊ฒฝ์ฌํญ ์ ์ฅ ๋ฐฉ๋ฒ (ModelContext์ ๊ฐ์ ์ ์ฅํ๊ณ ์ง์ ์ปจํ
์ด๋์ ์ปค๋ฐํ๋ผ๊ณ ์์ฒญ)
try context.save()
Modifying your data
- @Model macro modifies setters for change tracking and observation
- Updates automatically by the ModelContext
๋ชจ๋ธ ๊ฐ์ฒด์ ๋ํ ํ๋กํผํฐ ๊ฐ์ ๋ฐ๊พธ๋ ๊ฒ์ ํ์์ ํ๋กํผํฐ setter๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋งํผ์ด๋ ์ฝ์ต๋๋ค.
@Model ๋งคํฌ๋ก๋ ์ ์ฅ๋ ํ๋กํผํฐ๋ฅผ ์์ ํด ModelContext๊ฐ ๋ณ๊ฒฝ ์ฌํญ์ ์๋ ์ถ์ ํ๊ณ ๋ค์ ์ ์ฅ Operation์ ํฌํจ์ํค๋ ๊ฒ์ ๋์ต๋๋ค.
SwiftData ์ปจํ ์ด๋์ ์ปจํ ์คํธ, ์์ ์ ๋ํด์๋ โDive Deeper into SwiftDataโ์ธ์ ํ์ธ !
3. Use SwiftData with SwiftUI
- Seamless integration with SwiftUI
- Easy Configuration
- Automatically fetch data and update views
SwiftData๋ SwiftUI๋ฅผ ๊ณ ๋ คํ์ฌ ๋ง๋ค์ด์ก๊ธฐ์ ๋์ด ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ ์์ฃผ ์ฝ์ต๋๋ค. ๋ฐ๋ผ์ SwiftUI๋ SwiftData๋ฅผ ์ฌ์ฉํด๋ณผ ์ ์๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋๋ค.
SwiftData ์ปจํ ์ด๋๋ฅผ ์ค์ ํ๋ ๊ฒ์ด๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๋ทฐ ์ ๋ฐ์ดํธ๋ฅผ ํ๋ ๊ฒ์ด๋ ๊ด๋ จ๋ API๊ฐ ๊ตฌ์ถ๋์ด์๊ธฐ ๋๋ฌธ์, ์ SwiftUI scene, view modifiers๋ SwiftData ์ฑ ๊ตฌ์ถ์ ์์ํ ์ ์๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋๋ค.
View modifiers
- Leverage scene and view modifiers
- Configure data store with .modelContainer
- Propagated throughout SwiftUI environment
- SwiftUI will propagate your model context in its environment.
SwiftUI๋ฅผ ํตํด ๋ฐ์ดํฐ ์คํ ์ด ๊ตฌ์ฑ ์ต์ ๋ณ๊ฒฝ, ์คํ ์ทจ์ ์๋์ ์ฅ ํ ๊ธ์ด ๊ฐ๋ฅํ๋ฉฐ, SwiftUI๋ ํด๋น ํ๊ฒฝ์์ ๋ชจ๋ธ ์ปจํ ์คํธ๋ฅผ ์ ํํฉ๋๋ค.
SwiftUI ์ฝ๋๋ฅผ ํ์ธํด๋ณผ๊น์?
import SwiftData
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
// ...
}
}
}
}
}
์ผ๋จ ์ค์ ํ๊ณ ๋๋ฉด ๊ฐ์ฅ ์ฌ์ด ๋ฐ์ดํฐ ์ฌ์ฉ ๋ฐฉ์์ ์๋ก์ด @Query ํ๋กํผํฐ ๋ํผ์ ๋๋ค. ๋จ ํ ์ค์ ์ฝ๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ๋ฌด์์ด๋ ์ฝ๊ฒ ๋ก๋ฉํ๊ณ ํํฐ๋งํ ์ ์์ต๋๋ค.
Observing changes
- No need for @Published
- SwiftUI automatically refreshes
SwiftData๋ ๋ชจ๋ธ ํ๋กํผํฐ์ ๋ํด ์๋ก์ด observable ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
SwiftUI๋ observed ํ๋กํผํฐ์ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ์๋ก๊ณ ์นจํฉ๋๋ค.
SwiftUI์ SwiftData์ ํ๋ ์์ํฌ๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๋ฒ์ 'Build an app with SwiftData' ์ธ์ ์ ํ์ธํด ๋ณด์ธ์.
4. Wrap-up
- Define your schema using @Model
- Configure your ModelContainer
- Leverage SwiftUI and SwiftData
'๐ Apple > WWDC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftData] 03. Dive deeper into SwiftData(SwiftData ์์ธํ ์ดํด๋ณด๊ธฐ) (7) | 2024.09.02 |
---|---|
[SwiftData] 02. Model your schema with SwiftData(SwiftData๋ก ์คํค๋ง ๋ชจ๋ธ๋งํ๊ธฐ) (0) | 2024.08.20 |