SwiftData关联建立崩溃及重复实体生成问题咨询
问题背景
使用SwiftData + SwiftUI开发,定义了User、Book、Ownership三个关联实体,遇到两个核心问题:
- 先插入
User、Book到主上下文,再创建关联二者的Ownership时触发Illegal attempt to establish a relationship崩溃,全程仅使用一个ModelContext; - 调整为先实例化所有实体再插入上下文的顺序后,崩溃消失,但查询结果显示生成了两个
Book实体(代码仅调用一次Book初始化)。
实体模型代码
import SwiftData @Model class User { var name: String var ownerships: [Ownership]? init(name: String) { self.name = name } } @Model class Book { var title: String var ownerships: [Ownership]? init(title: String) { self.title = title } } @Model class Ownership { var user: User? var book: Book? var ownedDate: Date init(user: User?, book: Book?, ownedDate: Date = .now) { self.user = user self.book = book self.ownedDate = ownedDate } }
问题1:关联时触发上下文崩溃
测试代码(崩溃场景)
let container = try! ModelContainer(for: User.self, Book.self, Ownership.self) let context = container.mainContext // 先插入User和Book到上下文 let user = User(name: "Alice") context.insert(user) let book = Book(title: "Swift in Depth") context.insert(book) // 创建关联的Ownership时崩溃 let ownership = Ownership(user: user, book: book) context.insert(ownership) // 崩溃触发点
报错信息
Illegal attempt to establish a relationship 'user' between objects in different contexts (source: <User: 0x600000c44000> (entity: User; id: 0x600001f10000; store: <NSSQLCore: 0x600000f10000>), destination: <Ownership: 0x600000c44080> (entity: Ownership; id: 0x600001f10040; store: (null)))
原因
Xcode 15 Beta 5的SwiftData存在版本bug:当实体已插入上下文并被跟踪(即使未调用save()),创建关联实体时,SwiftData内部会错误判定新实体属于不同上下文,触发关系建立的非法操作。
解决方案
方案1:延迟关联设置,先插入Ownership再绑定关系
let container = try! ModelContainer(for: User.self, Book.self, Ownership.self) let context = container.mainContext let user = User(name: "Alice") context.insert(user) let book = Book(title: "Swift in Depth") context.insert(book) // 先创建无关联的Ownership并插入 let ownership = Ownership(user: nil, book: nil) context.insert(ownership) // 插入后再绑定关联关系 ownership.user = user ownership.book = book
方案2:调整顺序,先创建所有实体再统一插入(即问题2的前置操作)
问题2:实体重复创建
测试代码(重复场景)
let container = try! ModelContainer(for: User.self, Book.self, Ownership.self) let context = container.mainContext // 先实例化所有实体 let user = User(name: "Alice") let book = Book(title: "Swift in Depth") let ownership = Ownership(user: user, book: book) // 手动插入所有实体 context.insert(user) context.insert(book) context.insert(ownership) // 查询Book数量,结果为2 let books = try! context.fetch(FetchDescriptor<Book>()) print(books.count) // 输出2
原因
SwiftData在初始化关联实体(如Ownership)时,会自动将关联的User、Book插入上下文。此时手动再次调用context.insert(book),会触发Beta版本的重复插入bug,导致同一个Book实例被重复持久化。
解决方案
方案1:仅插入关联实体,依赖SwiftData自动处理关联对象的插入
let container = try! ModelContainer(for: User.self, Book.self, Ownership.self) let context = container.mainContext let user = User(name: "Alice") let book = Book(title: "Swift in Depth") let ownership = Ownership(user: user, book: book) // 仅插入Ownership,SwiftData会自动插入关联的user和book context.insert(ownership) let books = try! context.fetch(FetchDescriptor<Book>()) print(books.count) // 输出1
方案2:插入前检查实体是否已被上下文跟踪
let container = try! ModelContainer(for: User.self, Book.self, Ownership.self) let context = container.mainContext let user = User(name: "Alice") let book = Book(title: "Swift in Depth") let ownership = Ownership(user: user, book: book) // 仅插入未被跟踪的实体 if !user.isInserted { context.insert(user) } if !book.isInserted { context.insert(book) } context.insert(ownership) let books = try! context.fetch(FetchDescriptor<Book>()) print(books.count) // 输出1
总结
两个问题均为Xcode 15 Beta 5中SwiftData的已知版本bug,后续正式版会修复。当前可通过调整插入顺序、延迟关联设置、避免重复插入的方式规避问题。
内容的提问来源于stack exchange,提问作者f3dm76

