@Relationship 是 SwiftData 框架提供的一个属性包装器,用于声明实体之间的关系。它是 SwiftData 中的核心功能之一,允许开发者在定义数据模型时,直观地描述和管理实体之间的关联。
@Relationship 的用途
定义数据模型之间的关联:
用于描述两个实体(比如 User 和 Job)之间的逻辑关系。
管理数据依赖:
定义数据在增删改查时的行为(例如级联删除)。
简化代码:
自动处理关联的存储、查询和关系维护,无需手动管理复杂的外键。
使用方式
1、一对多关系
import SwiftData
@Model
class User {
var name: String
var city: String
@Relationship(deleteRule: .cascade) var jobs = [Job]() // 一对多关系
init(name: String, city: String) {
self.name = name
self.city = city
}
}
@Model
class Job {
var name: String
var priority: Int
@Relationship var owner: User? // 多对一关系
init(name: String, priority: Int, owner: User? = nil) {
self.name = name
self.priority = priority
self.owner = owner
}
}
解释:
在 User 类中,@Relationship 指定了 jobs 是一组与 Job 相关的实例(多对一)。
deleteRule: .cascade 指定了当 User 被删除时,所有与之关联的 Job 也会被删除。
在 Job 类中,owner 是一个 User 实例的引用,描述了 Job 与其创建者的关系。
2、一对一关系
@Model
class Profile {
var bio: String
@Relationship(deleteRule: .nullify) var user: User?
init(bio: String, user: User? = nil) {
self.bio = bio
self.user = user
}
}
@Model
class User {
var name: String
var city: String
@Relationship var profile: Profile? // 一对一关系
init(name: String, city: String, profile: Profile? = nil) {
self.name = name
self.city = city
self.profile = profile
}
}
解释:
Profile 和 User 的关系是一对一。
deleteRule: .nullify 表示当 User 被删除时,不会删除 Profile,而是将其 user 属性设置为 nil。
3、多对多关系
@Model
class Student {
var name: String
@Relationship var courses: [Course] = [] // 多对多关系
init(name: String) {
self.name = name
}
}
@Model
class Course {
var title: String
@Relationship var students: [Student] = [] // 多对多关系
init(title: String) {
self.title = title
}
}
解释:
Student 和 Course 之间是多对多关系,每个学生可以选修多个课程,每个课程也可以被多个学生选修。
@Relationship 自动管理双向关系。
具体实例
@Model
class User {
var name: String
var city: String
var joinDate: Date
@Relationship(deleteRule: .cascade) var jobs = [Job]()
init(name: String, city: String, joinDate: Date) {
self.name = name
self.city = city
self.joinDate = joinDate
}
}
Model
class Job {
var name: String
var priority: Int
var owner: User?
init(name: String, priority: Int, owner: User? = nil) {
self.name = name
self.priority = priority
self.owner = owner
}
}
上面的代码是一对多的关系,一个User类对应多个Job类。
在视图当中,可以先定义一个@Query var job和临时储存User的属性。
@Query var job: [Job]
@State var modelUser: User?
新增一个创建按钮和删除按钮,并显示Job实例的数量:
var body: some View {
VStack {
Button(action: {
addSample()
}, label: {
Text("新增job数据")
.foregroundColor(Color.white)
.padding(10)
.background(Color.blue)
.cornerRadius(8)
})
Button(action: {
deleteSample()
}, label: {
Text("删除user1元素")
.foregroundColor(Color.white)
.padding(10)
.background(Color.blue)
.cornerRadius(8)
})
Text("目前jobs数量为:\(job.count)")
}
}
这两个按钮分别对应的是addSample()和deleteSample()方法。
当点击“新增job数据”按钮时,会调用addSample()方法,并创建一个User实例,在同时创建两个job实例:
func addSample() {
// 创建新的 User 和 Job
let user1 = User(name: "Piper Chapman", city: "New York", joinDate: .now)
let job1 = Job(name: "Organize sock drawer", priority: 3)
let job2 = Job(name: "Make plans with Alex", priority: 4)
modelContext.insert(user1)
user1.jobs.append(job1)
user1.jobs.append(job2)
}
在上下文中插入新增的user实例,并将两个job实例追加到user实例的jobs属性中。
这里实现的是一个user实例对应了两个job实例。前面在User类中提到,jobs使用的是@Relationship属性包装器:
@Relationship(deleteRule: .cascade) var jobs = [Job]()
这里的deleteRule: .cascade表示删除父对象时,会自动删除所有关联的子对象。
因此,当我们删除user实例时,对应的也会删除关联的两个job实例。
为了实现这一个一对多的关系以及删除的效果,首先点击“新增job数据”按钮。
可以发现,在视图中新增了一个user实例,并且显示当前的jobs数量为2。
也可以在数据库中看到新增的实例数据。
接着是点击删除按钮,并调用deleteSample()方法:
func deleteSample() {
do {
let users = try modelContext.fetch(FetchDescriptor<User>())
for user in users {
if user.name == "Piper Chapman" {
modelContext.delete(user)
}
}
} catch {
print("报错了")
}
}
deleteSample()方法会从modelContext上下文中找到user实例中名为“Piper Chapman”的实例并删除掉。
这时可以从视图或者数据库中看到,无论是user还是job实例都会被删除。
这就是前面提到的一对多关系,一个user对应多个job,当删除user时,对应的job也会删除。
删除规则(deleteRule)
@Relationship 提供了四种删除规则,用于控制当父对象被删除时,子对象应如何处理:
.cascade:删除父对象时,自动删除所有关联的子对象。
.nullify:删除父对象时,将子对象的关系置为 nil。
.deny:禁止删除父对象,除非所有子对象已被解除关系。
.noAction:删除父对象时,不对子对象执行任何操作(开发者需自行处理可能的孤立数据)。
优势
自动化: 通过简单的声明语句自动处理复杂的关系逻辑。
安全性: 删除规则可确保数据模型的完整性,避免孤立或无效数据。
直观性: 开发者可以轻松建模,符合日常逻辑理解。
注意事项
1、双向关系
必须在两个实体的关系中明确设置 @Relationship,才能使关系保持同步。
@Relationship var owner: User?
@Relationship var jobs: [Job]
2、默认删除规则
如果未设置 deleteRule,SwiftData 会默认采用 .noAction。
3、关系必须匹配
数据一致性由 SwiftData 自动管理,确保关系的正确性(如一对多、一对一等)。
总结
@Relationship 是 SwiftData 中描述实体关系的核心工具。通过它,开发者可以轻松地定义一对多、一对一、多对多等各种关系,并使用 deleteRule 管理关联对象的生命周期和行为。这种设计简化了复杂的数据库操作,并提供了更强的数据一致性保证。
完整的JobView代码
import Foundation
import SwiftData
import SwiftUI
struct JobView: View {
@Environment(\.modelContext) var modelContext
@Query var job: [Job]
func addSample() {
// 创建新的 User 和 Job
let user1 = User(name: "Piper Chapman", city: "New York", joinDate: .now)
let job1 = Job(name: "Organize sock drawer", priority: 3)
let job2 = Job(name: "Make plans with Alex", priority: 4)
modelContext.insert(user1)
user1.jobs.append(job1)
user1.jobs.append(job2)
}
func deleteSample() {
do {
let users = try modelContext.fetch(FetchDescriptor<User>())
for user in users {
if user.name == "Piper Chapman" {
modelContext.delete(user)
}
}
} catch {
print("报错了")
}
}
var body: some View {
VStack {
Button(action: {
addSample()
}, label: {
Text("新增job数据")
.foregroundColor(Color.white)
.padding(10)
.background(Color.blue)
.cornerRadius(8)
})
Button(action: {
deleteSample()
}, label: {
Text("删除user1元素")
.foregroundColor(Color.white)
.padding(10)
.background(Color.blue)
.cornerRadius(8)
})
Text("目前jobs数量为:\(job.count)")
}
}
}
#Preview {
JobView()
.modelContainer(for: [User.self, Job.self])
}