predicates๋ ํ๊ตญ์ด๋ก ๋ฒ์ญํ๋ฉด ์ ์ด๋ผ๊ณ ๋ฒ์ญํ ์ ์๋๋ฐ ์ด๋ ๋ ผ๋ฆฌ์ ์ธ ์กฐ๊ฑด์์ ์๋ฏธํจ. SwiftData๋ predicates๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํ๊ณ ์กฐ๊ฑด์ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ์ ์๋ค.
๋ ผ๋ฆฌ์ ์กฐ๊ฑด์ ์ ์ํ ์ ์๊ณ ๊ตฌ์ฒด์ ์ธ ๋ฐ์ดํฐ๋ฅผ ํ๋ํ ์ ์๋ค.
SwiftData์ Schema๋ฅผ ์ ์ํ๊ธฐ ์ํด Model์ ๊ตฌ์ฑํ ๋ enum ์ด๋ image ํ์ ์ ์ฌ์ฉํ ์๋ ์์.
Relationship
๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ์์ ํผ์ ์กด์ฌํ๋ ๊ฐ์ฒด๋ ์๊ณ , ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ทธ ๊ด๊ณ๋ฅผ ๊ธฐ์ ํ๋ ๊ฒ์ ์ค์ํ๋ค.
๋ค:๋ค ๊ด๊ณ
A๋ผ๋ ๋ชจ๋ธ์ด B๋ชจ๋ธ์ ์ฌ๋ฌ๊ฐ ๊ฐ์ง๊ณ ์์ ์ ์๊ณ , B๋ผ๋ ๋ชจ๋ธ์ด A๋ผ๋ ๋ชจ๋ธ์ ์ฌ๋ฌ๊ฐ ๊ฐ์ง ์ ์์๋ ๋ค:๋ค ๊ด๊ณ๋ผ๊ณ ํ๋ค.
/// ์ ์ ์ ๋ณด
@Model class User {
/// ์ ์ ์ ๊ณ ์ ๊ฐ
@Attribute(.unique) var id: UUID = UUID()
/// ์ ์ ์ด๋ฆ
var name: String
/// ์ ์ ๋์ด
var age: Int
/// ์ ์ ์์ผ
var birth: Date
/// ์ ์ ์ ๊ด์ฌ์ฌ. ์ฌ๋ฌ๊ฐ ์ ํ ๊ฐ๋ฅ
var interests: [Interest] = []
init(name: String, age: Int, birth: Date, closeUser: [User]) {
self.name = name
self.age = age
self.birth = birth
}
}
/// ์ ์ ๊ด์ฌ์ฌ
@Model class Interest {
/// ๊ด์ฌ์ฌ ๊ณ ์ ๊ฐ
@Attribute(.unique) var id: UUID = UUID()
/// ๊ด์ฌ์ฌ ๋ช
var title: String
/// ํด๋น ๊ด์ฌ์ฌ์ ๊ด์ฌ์๋ ์ ์ ๋ชฉ๋ก
var user: [User] = []
init(title: String) {
self.title = title
}
}
์ ์ฝ๋์์ ์ฌ์ฉ์๋ ๊ด์ฌ์ฌ๋ฅผ ์ฌ๋ฌ๊ฐ ์ ํํ ์ ์๊ณ , ๊ด์ฌ์ฌ๋ ํด๋น ๊ด์ฌ์ฌ์ ๊ด์ฌ์๋ ์ฌ์ฉ์ ๋ชฉ๋ก์ ๊ฐ์ง ์ ์๋ค.
1:๋ค ๊ด๊ณ
ํ๋์ ๋ชจ๋ธ์ ๋ค๋ฅธ ํ์ ์ ๋ชจ๋ธ์ ์ฌ๋ฌ๊ฐ ๊ฐ์ง ์ ์์ ๋ 1:๋ค ๊ด๊ณ๋ผ๊ณ ํ๋ค.
/// ์ ์ ์ ๋ณด
@Model class User {
/// ์ ์ ์ ๊ณ ์ ๊ฐ
@Attribute(.unique) var id: UUID = UUID()
/// ์ ์ ์ด๋ฆ
var name: String
/// ์ ์ ๋์ด
var age: Int
/// ์ ์ ์์ผ
var birth: Date
/// ์ ์ ์ ๊ด์ฌ์ฌ. ์ฌ๋ฌ๊ฐ ์ ํ ๊ฐ๋ฅ
var interests: [Interest] = []
/// ์์๋ ํ์ฌ
var company: Company? = nil
init(name: String, age: Int, birth: Date, closeUser: [User]) {
self.name = name
self.age = age
self.birth = birth
}
}
/// ํ์ฌ ์ ๋ณด
@Model class Company {
/// ํ์ฌ ๊ณ ์ ๊ฐ
@Attribute(.unique) var id: UUID = UUID()
/// ํ์ฌ๋ช
var name: String
/// ํด๋น ํ์ฌ์ ์์๋ ์ ์ ๋ชฉ๋ก
var user: [User] = []
init(name: String) {
self.name = name
}
}
์ฌ์ฉ์๋ ํ๋์ ํ์ฌ์ ์์๋๊ฑฐ๋ ์์๋์ง ์์ ์ ์์ผ๋ฉฐ ํ์ฌ๋ ์์๋ ์ฌ์ฉ์์ ๋ชฉ๋ก์ ๊ฐ์ง๊ณ ์๋ค.
๋ชจ๋ธ ํด๋์ค ๊ฐ์ Relationship
/// ํด๋น ํ์ฌ์ ์์๋ ์ ์ ๋ชฉ๋ก
/// ํด๋น ํ์ฌ๊ฐ ์ญ์ ๋๋ฉด ํ์ฌ์ ์์๋ ์ฌ์ฉ์ ๋ชฉ๋ก์ด ํจ๊ป ์ญ์ ๋๋ค.
@Relationship(deleteRule: .cascade, inverse: \User.company)
var user: [User] = []
deleteRule์ ํตํด cascade๋ก ์ง์ ํ๋ฉด company๊ฐ ์ญ์ ๋ ๋ ํด๋น company์ ์์๋ User๋ ํจ๊ป ์ญ์ ๋๋ค.
inverse ํ๋กํผํฐ๋ ํด๋น ๋ชจ๋ธ๊ณผ ์ด์ ๊ด๋ จ๋ ๋ชจ๋ธ๊ฐ์ ๊ด๊ณ๋ฅผ ์ ๊ณตํ๋ค. User์ Company์ ํด๋น ๋ชจ๋ธ๊ฐ์ ๊ด๊ณ๋ฅผ inverse ํคํจ์ค๋ก ์ ๋ฌ ํ ์ ์๋ค.
๋ง์ฝ์ ์ญ์ ์ต์ ์ ์ํ์ง ์์ผ๋ฉด inverse๋ง ์ ๊ณตํ๋ฉด ๋๋ค.
/// ํด๋น ํ์ฌ์ ์์๋ ์ ์ ๋ชฉ๋ก
@Relationship(inverse: \User.company)
var user: [User] = []
SwiftData๋ ์๋์ ์ผ๋ก ๊ทธ ๊ด๊ณ๋ฅผ ์ถ๋ก ํ๋ค. ๋๋ฌธ์ inverse๋ฅผ ์ ๊ณตํ์ง ์๋๋ผ๋ SwiftData๊ฐ ์๋์ผ๋ก ๋ชจ๋ธ๊ฐ์ ๊ด๊ณ๋ฅผ ์ ์ถํ ์ ์๋ค. ๋ฐ๋ผ์ model container๋ฅผ ๊ตฌ์ถํ ๋ ๋ชจ๋ ์คํค๋ง๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด ์๋ ํ์ ์คํค๋ง๋ฅผ ๋ชจ๋ ํฌํจํ๊ณ ์๋ ์์ ์คํค๋ง ํ๋๋ฅผ ์ ๋ฌํ๋ฉด ๋ชจ๋ ๊ด๊ณ๋ฅผ ์ถ๋ก ํ ์ ์๊ฒ ๋๋ค.
user์ company๋ฅผ ์ง์ ํ๋ฉด company์ user list์๋ user๊ฐ ์๋์ผ๋ก ์ถ๊ฐ๋๋ค. ์ด๋ swiftdata๊ฐ ์๋์ผ๋ก ๊ทธ ๊ด๊ณ๋ฅผ ์ถ๋ก ํ ์ ์๊ธฐ ๋๋ฌธ. ๋ง์ฐฌ๊ฐ์ง๋ก user.company๋ฅผ nil๋ก ํ๋ฉด ๊ทธ ๊ด๊ณ ์ญ์ ์๋์ผ๋ก ๋์ด์ง๋ค.
deleteRule
// User Model
var company: Company? = nil
// Company Model
@Relationship(deleteRule: .nullify, inverse: \User.company) // ์ฌ๊ธฐ์ ์ง์ ํ๋ deleteRule์ ํ์ฌ๋ฅผ ์ง์ ์ ๋ User๋ฅผ ์ด๋ป๊ฒ ํ๊ฒ ๋๋ ๋ป
var users: [User] = []
๋์์ ํ์ธํด ๋ดค์ ๋ deleteRule์ ๋ช ์ํ์ง ์์๊ณ , ๋ ๊ด๊ณ๊ฐ ํ์์์๊ฐ ์๋ ๋ nullify ์ต์ ์ผ๋ก ์ง์ ๋๋ ๊ฒ ๊ฐ๋ค.
๋ง์ฝ ์ฌ๊ธฐ์ cascade๋ก ์ง์ ํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
nil์ด ๊ธฐ๋ณธ์ผ๋ก ์ค์ ๋๋ ๊ด๊ณ์์๋ nullify๋ฅผ ์ค์ ํด์ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ์ฃผ์ํด์ผ ํจ
Cascade๋ฅผ ์ฌ์ฉํ ๋๋ ๋ชจ๋ธ ๊ตฌ์ฑ์ ์ ๊ฒฝ ์จ์ผ ํ๋ค.
@Model
class Book {
/// ๊ณ ์ ๊ฐ
// @Attribute(.unique) var id: UUID = UUID()
/// ์ฑ
์ด๋ฆ
var name: String
/// ์์ ๋์๊ด
@Relationship(inverse: \Library.books)
var library: Library?
init(name: String) {
// self.id = UUID()
self.name = name
}
}
๊ณ ์ ๊ฐ์ ์์ฑํ์ง ์๋๋ก ํ๋ค. cascade์ต์ ์ผ๋ก ์ง์ ์ ๋ ์๋ฌ๊ฐ ๋ฐ์ํจ
@Model
class Library {
/// ๊ณ ์ ๊ฐ
@Attribute(.unique) var id: UUID = UUID()
/// ๋์๊ด ์ด๋ฆ
var name: String
/// ๋์๊ด์ ๋ฐฐ์น๋ ์ฑ
@Relationship(deleteRule: .cascade)
var books: [Book] = []
init(name: String) {
self.id = UUID()
self.name = name
}
}
relationship์ cascade๋ก ๊ฑธ์ด๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด Library๊ฐ ์ฌ๋ผ์ง ๋ ์ฑ ์ด ํจ๊ป ์ฌ๋ผ์ง๋ค.
์ฐธ์กฐ ๋ฌด๊ฒฐ์ฑ
Company์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๋ฉด user์ company ์ ๋ณด๋ ๋ณ๊ฒฝ๋ ๊น?
Transient
/// ํ์ฌ ๋ชฉ๋ก์์ ์ ํ๋ ํ์ฌ๋ฅผ ํ์ํ๊ธฐ ์ํด ์ฌ์ฉ
@Transient
var isChecked = false
Transient๋ฅผ ์ฌ์ฉํ๋ฉด SwiftData๋ ํด๋น ๊ฐ์ ๊ด๋ฆฌํ์ง ์๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋๋ ๊ฐ์ด ์๋ ํด๋น ๊ฐ์ฒด์ ๋น์ฆ๋์ค ๋ก์ง์์ ์ฌ์ฉ๋ ๊ฐ์ ์ ์ํ๋ค.
Predicates
์ ์ด๋ ๋ ผ๋ฆฌ์ ์ธ ์กฐ๊ฑด์์ด๋ฉฐ ์ด ์กฐ๊ฑด์ด true์ธ์ง false์ธ์ง๋ฅผ ํ๋ณํ๋ค. predicates๋ฅผ ์ฌ์ฉํด์ filtering์ ์ํํ๊ฑฐ๋ Sorting ํ์ฌ ๊ตฌ์ฒด์ ์ธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ๊ฐ์ ธ์ฌ ์ ์๋ค.
์ ์ฅ๋ ๋ฐ์ดํฐ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์กฐ์ํ๊ธฐ ์ํ ์กฐ๊ฑด์ ์ ์ ํ ์ ์๋ค. SwiftData ์ฟผ๋ฆฌ์์ predicate๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ SwiftData๋ ์ด๋ฅผ ์ํด @Query ํ๋กํผํฐ ๋ํผ๋ฅผ ์ ๊ณตํ๋ค. @Query ํ๋กํผํฐ๋ํผ๋ฅผ ์ฌ์ฉํด์ ํํฐ์ ์ ๋ ฌ๋ฅผ ์ง์ ํ ์ ์๋ค.
@Query(filter: #Predicate<User> { $0.age > 20 },
sort: [SortDescriptor(\User.birth)])
var sortedUser: [User]
Predicate๋ฅผ ์ฌ์ฉํด์ ์ฌ์ฉ์์ ๋์ด๊ฐ 20์ด์ ๋์๋์ง ํ์ธํด์ ํด๋น ๊ฐ์ด true์ธ user๋ฅผ ๊ฐ์ง๊ณ ์จ๋ค. ๊ทธ ์ ์ ๋ฅผ ์์ผ๋ก ์ ๋ ฌํ๋๋ก ํ๋ ค๋ฉด ์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋๋ค.
#Predicate๋ฅผ predicate macro๋ผ๊ณ ํ๋ฉฐ ํํฐ๋ง ์กฐ๊ฑด์ ์ ์ํ๊ธฐ ์ํด ์ฌ์ฉํ๋ค.
sort๋ SortDescriptor ๊ตฌ์กฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
Predicates๋ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ์กฐ๊ฑด์ ๊ธฐ์ ํ ์ ์๋๋ฐ ์กฐ๊ฑด์๊ณผ ๋ค์ํ ํํ์ predicate ํด๋ก์ ๋ด๋ถ์์ ํํฐ๋ง ์กฐ๊ฑด์์ ์ ์ ํ ์ ์๋ค.
@Query private var queriedUser: [User]
var queriedUserComputedProperty: [User] {
let startDate = Date(timeIntervalSinceReferenceDate: -123456789.0)
let endDate = Date()
return queriedUser.filter({ $0.birth > startDate && $0.birth < endDate })
}
Predicate์ ์กฐ๊ฑด์์ ๋ฃ๊ธฐ ์ํด์๋ Query property wrapper ์์ฒด๊ฐ lazy var ๋ก ์ ์ธ์ด ์๋์ static ๊ฐ์ ์ฌ์ฉํ๋๋ฐ ๋ด๋ถ์ globalํจ์๋ ์ธ ์ ์์
Date ํจ์ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํด์ Computed Property๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์์
๋ณต์กํ Predicate๋ฅผ ์ํ ํํ์
@Query(filter: #Predicate<User> { user in
user.name.contains("kim") &&
user.interests.contains { $0.title == "๋ฑ์ฐ" } == true
}) var query4: [User]
๊ฒ์๊ธฐ๋ฅ ์ด์ฉํ๊ธฐ
@State private var userToEdit: User?
@State private var searchString: String = ""
// ์๋ต
UserListView(searchString: searchString, userToEdit: $userToEdit)
.searchable(text: $searchString)
.sheet(item: $userToEdit) { user in
UpdateUserSheetView(user: user)
}
struct UserListView: View {
@Environment(\.modelContext) var context
@Query(sort: \User.age, order: .forward) private var users: [User]
@Binding var userToEdit: User?
init(searchString: String, userToEdit: Binding<User?>) {
if searchString.count > 0 {
self._users = Query(filter: #Predicate<User> {
$0.name.contains(searchString)
}, sort: \User.age, animation: .easeInOut)
} else {
self._users = Query(sort: \User.age, animation: .easeInOut)
}
self._userToEdit = userToEdit
}
var body: some View {
List {
ForEach(users) { user in
HStack {
Text(user.name)
Text("\(user.age)")
Text("\(user.birth.formatted())")
}
.onTapGesture {
userToEdit = user
}
}
.onDelete{ indexSet in
print("์ธ๋ฑ์ค : ", indexSet)
for index in indexSet {
context.delete(users[index])
}
}
} //: List
.overlay {
if users.isEmpty {
Text("์ฌ์ฉ์๋ฅผ ์ถ๊ฐํด ์ฃผ์ธ์")
}
}
}
}
'iOS ๐ > Library' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftUI/TipKit] ์ฌ์ฉ์์๊ฒ Tip ์ ๊ณตํ๊ธฐ (1) | 2023.12.02 |
---|---|
[SwiftUI/VisionKit] CSํ์ ๋์๋๋ฆฌ์! ์๋ฅ ์ค์บ๊ณผ ์ด๋ฏธ์ง ๊ธ์ ์ธ์ (0) | 2023.11.16 |