Since iOS 27.2 (24B119), SwiftData no longer sets the inverse (child → parent) relationship for the children of a model that hasn't been inserted yet when that model is attached to an already-saved model. The children are saved with a nil parent, so the parent's to-many relationship reads back empty. This happens in the same ModelContext and also after reopening the store.
The same code works correctly on iOS 27.0 (24A5390f).
@Model final class Ledger {
@Relationship(deleteRule: .cascade, inverse: \Transaction.ledger)
var transactions: [Transaction] = []
}
@Model final class Transaction {
var ledger: Ledger?
@Relationship(deleteRule: .cascade, inverse: \Item.transaction)
var items: [Item] = []
init(items: [Item]) { self.items = items }
}
@Model final class Item {
var transaction: Transaction?
}
// Inside a @ModelActor; `ledger` is fetched and already saved.
let transaction = Transaction(items: [Item(), Item()]) // not inserted
ledger.transactions.append(transaction)
// iOS 27.0: item.transaction === transaction for every item
// iOS 27.2: item.transaction == nil for every item
try modelContext.save()
// iOS 27.2: transaction.items.count == 0 (expected 2); both items are orphaned rowsThe inverse is already nil before save(), so this isn't a save or flush problem. The link just isn't set when the graph is attached.
Affected patterns on 27.2
• persisted.list.append(uninsertedParentWithChildren)
• persisted.list[index] = uninsertedParentWithChildren
• Reassigning persisted.list = [...] when the new models have their own child arrays (grandchildren end up orphaned)
• Models with several to-many arrays of the same child type
Not affected
• A graph that's entirely new, including the root
• To-one relationships set in init
• Models inserted into the context before the relationship is set
Toolchain ruled out. I built the repro on the same iPhone 13 (iOS 27.2) with three toolchains: xcode 26.6, 27.1 and 27.2
Workaround. Insert the children and set the inverse explicitly before saving:
modelContext.insert(transaction)
for item in transaction.items {
modelContext.insert(item)
item.transaction = transaction
}
ledger.transactions.append(transaction)
try modelContext.save()You have to do this at every level, grandchildren included.
Has anyone else seen this on 27.2? And can someone from the SwiftData team confirm whether this is an intended behavior change or a regression?
Feedback: FB25355425 (includes a standalone repro project, reports from iOS 27.0 and 27.2, and a sysdiagnose)
0
0
46