import SwiftData
// A: DTS forum code — to-many arrays default to []
@Model final class LedgerA {
@Relationship(deleteRule: .cascade, inverse: \TransactionA.ledger) var transactions: [TransactionA] = []
init() {}
}
@Model final class TransactionA {
var ledger: LedgerA?
@Relationship(deleteRule: .cascade, inverse: \ItemA.transaction) var items: [ItemA] = []
init(items: [ItemA]) { self.items = items }
}
@Model final class ItemA { var transaction: TransactionA?; init() {} }
// B: same, but no `= []` default
@Model final class LedgerB {
@Relationship(deleteRule: .cascade, inverse: \TransactionB.ledger) var transactions: [TransactionB]
init() { transactions = [] }
}
@Model final class TransactionB {
var ledger: LedgerB?
@Relationship(deleteRule: .cascade, inverse: \ItemB.transaction) var items: [ItemB]
init(items: [ItemB]) { self.items = items }
}
@Model final class ItemB { var transaction: TransactionB?; init() {} }
// C: optional to-many arrays
@Model final class LedgerC {
@Relationship(deleteRule: .cascade, inverse: \TransactionC.ledger) var transactions: [TransactionC]?
init() { transactions = [] }
}
@Model final class TransactionC {
var ledger: LedgerC?
@Relationship(deleteRule: .cascade, inverse: \ItemC.transaction) var items: [ItemC]?
init(items: [ItemC]) { self.items = items }
}
@Model final class ItemC { var transaction: TransactionC?; init() {} }
@MainActor
func runInverseRepro() throws {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
// A — default []
let contextA = ModelContext(try ModelContainer(for: LedgerA.self, configurations: config))
contextA.insert(LedgerA())
try contextA.save()
if let ledger = try contextA.fetch(FetchDescriptor<LedgerA>()).first {
let items = [ItemA(), ItemA()]
ledger.transactions.append(TransactionA(items: items)) // transaction not inserted
print("A (= []) nil inverses before save:", items.filter { $0.transaction == nil }.count)
}
// B — no default
let contextB = ModelContext(try ModelContainer(for: LedgerB.self, configurations: config))
contextB.insert(LedgerB())
try contextB.save()
if let ledger = try contextB.fetch(FetchDescriptor<LedgerB>()).first {
let items = [ItemB(), ItemB()]
ledger.transactions.append(TransactionB(items: items))
print("B (no []) nil inverses before save:", items.filter { $0.transaction == nil }.count)
}
// C — optional
let contextC = ModelContext(try ModelContainer(for: LedgerC.self, configurations: config))
contextC.insert(LedgerC())
try contextC.save()
if let ledger = try contextC.fetch(FetchDescriptor<LedgerC>()).first {
let items = [ItemC(), ItemC()]
ledger.transactions?.append(TransactionC(items: items))
print("C (optional) nil inverses before save:", items.filter { $0.transaction == nil }.count)
}
}Same code
27.2
A (= []) nil inverses before save: 0
B (no []) nil inverses before save: 2
C (optional) nil inverses before save: 027.0
A (= []) nil inverses before save: 0
B (no []) nil inverses before save: 0
C (optional) nil inverses before save: 0
Topic:
App & System Services
SubTopic:
SwiftData
Tags: