У меня возникла проблема со вставкой моей модели и ее дочерних элементов «один ко многим» с помощью SwiftData. Несколько дней назад у меня была аналогичная проблема, которую решил @Sweeper. Благодаря его ответу я смог решить свою проблему; Но при попытке применить те же изменения в этом коде. Это не работает.
import SwiftUI
import SwiftData
@Model
public final class MyModel : CustomStringConvertible {
var uuid: String
@Relationship(deleteRule: .cascade, inverse: \MyModelField.model) var fields = [MyModelField]()
init(uuid: String) {
self.uuid = uuid
}
public var description: String {
return "\(self.uuid)(\(self.fields))"
}
}
@Model
public final class MyModelField : CustomStringConvertible {
let key: String
let value: String
var model: MyModel?
init(_ model: MyModel, _ key: String, _ value: String) {
self.model = model
self.key = key
self.value = value
}
public var description: String {
return "\(key):\(value)"
}
}
@ModelActor
public actor LocalDatabaseService {
public static let shared = LocalDatabaseService()
let schema = Schema([ MyModel.self, MyModelField.self ])
public init() {
self.modelContainer = try! ModelContainer(for: self.schema)
let context = ModelContext(modelContainer)
self.modelExecutor = DefaultSerialModelExecutor(modelContext: context)
}
public func createMyModel(uuid: String) throws {
let modelContext = self.modelContext
let myModel = MyModel(uuid: uuid)
let field1 = MyModelField(myModel, "hello", "world")
let field2 = MyModelField(myModel, "bonjour", "monde")
modelContext.insert(myModel)
// Dummy print in case the garbage collector has removed non used MyModelField
print("field1:\(field1) - field2:\(field2)")
try modelContext.save()
}
func dumpMyModels() throws {
let fetchDescriptor = FetchDescriptor<MyModel>()
let myModels = try modelContext.fetch(fetchDescriptor)
print("# Dump:\(myModels)")
}
}
struct ContentView: View {
var body: some View {
Task {
print("# List:")
try await LocalDatabaseService.shared.dumpMyModels()
print("# Add:")
let id = "9C66CA5B-D91C-480F-B02C-2D14EEB49902"
try await LocalDatabaseService.shared.createMyModel(uuid: id)
print("# List:")
try await LocalDatabaseService.shared.dumpMyModels()
print("DONE")
}
return VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
Я подтверждаю, что не вижу никаких SQL-запросов для записей MyModelField.
Что я вижу:
# List:
# Dump:[]
# Add:
field1:hello:world - field2:bonjour:monde
# List:
# Dump:[9C66CA5B-D91C-480F-B02C-2D14EEB49902([])]
DONE
Чего я ожидал:
# List:
# Dump:[]
# Add:
field1:hello:world - field2:bonjour:monde
# List:
# Dump:[9C66CA5B-D91C-480F-B02C-2D14EEB49902(["hello":"world","bonjour":"monde"])]
DONE





Я нашел несколько обходных путей для решения моих проблем. С помощью этих двух обходных путей я получил ожидаемый результат:
MyModelField:init(_ model: MyModel, _ key: String, _ value: String) {
self.key = key
self.value = value
self.model = model // <---- Must be set at the end!!! It is documented somewhere?
}
MyModelField вставляется вместе с ModelField, а последующие необходимо вставлять отдельно...let myModel = MyModel(uuid: uuid)
let field1 = MyModelField(myModel, "hello", "world")
modelContext.insert(myModel)
let field2 = MyModelField(myModel, "bonjour", "monde")
modelContext.insert(field2)
try modelContext.save()
Если у кого-то есть официальная документация по этим двум обходным путям, я буду рад ее принять!
Кажется, есть несколько способов обойти эту проблему. Один из способов - не вставлять myModel, а вставлять и поле1, и поле2, и это сработало (без реализации #1). Другой способ — вставить MyModel, а затем выполнить myModel.fields.append над полями (не вставляя их), и это сработает. В целом, похоже, сейчас есть некоторые проблемы с отношениями в SwiftData.
@JoakimDanielson воспроизводимый пример находится в вопросе - в первом фрагменте кода. Вывод идет оттуда.