Вот как я инициализирую контейнер модели:
import SwiftUI
import SwiftData
@main
struct MyApp: App {
let modelContainer: ModelContainer
init() {
do {
modelContainer = try ModelContainer(for: MyModel.self)
} catch {
fatalError("Could not initialize ModelContainer")
}
}
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(modelContainer)
}
}
}
и это остальная часть кода, которая может воспроизвести проблему:
import SwiftUI
import SwiftData
enum DayOfWeek: Identifiable, Hashable, Codable {
var id: Self { self }
case sunday, monday, tuesday, wednesday, thursday, friday, saturday
}
enum FrequencyType: Identifiable, Hashable, Codable {
var id: Self { self }
case daily(interval: Int)
case specificDaysOfWeek([DayOfWeek])
}
@Model
final class MyModel: Identifiable {
init() {
self.frequency = .daily(interval: 1)
}
@Attribute(.unique)
private(set) var id: String = UUID().uuidString
private(set) var frequency: FrequencyType
func setFrequency(_ frequency: FrequencyType) {
self.frequency = frequency
}
}
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
private let model = MyModel()
var body: some View {
Text("Content View")
.onAppear {
modelContext.insert(model)
model.setFrequency(.specificDaysOfWeek([.friday, .monday]))
}
}
}
происходит то, что когда я делаю что-то вроде:
model.setFrequency(.specificDaysOfWeek([.friday, .monday]))
приложение вылетает с такой ошибкой:
CoreData: ошибка: SQLCore sendRequest: запрос на обработку исключений: <NSSQLSaveChangesRequestContext: 0x60000374c300> , [<Foundation.__NSSwiftData 0x600000c6e8b0> valueForUndefinedKey:]: этот класс не соответствует кодированию значений ключей для ключа _buffer. с информация пользователя { NSTargetObjectUserInfoKey = {длина = 29, байт = 0x5b7b2266 72696461 79223a7b 7d7d2c7b ... 6179223a 7b7d7d5d }; NSUnknownUserInfoKey = "_buffer"; }
Я не понимаю, откуда это _buffer
и почему происходит сбой?
Обновлено:
Вот что я получу, если включу отладку Core Data (уровень 3):
Структуры SwiftUI, такие как App и View, не должны создавать объекты типа ModelContainer
в своей инициализации или теле. Вам нужно переместить его в действие типа onAppear
, иначе это утечка памяти и в вашем случае выглядит как сбой. Альтернативно воспользуйтесь функцией удобства .modelContainer(for: MyModel.self)
, которая доставит объект в нужное для вас время.
@malhal Если вы попытаетесь использовать то, что вы предложили, вы увидите ошибку: Fatal error: failed to find a currently active container for MyModel
. Вот почему я инициализировал его таким образом, просто чтобы создать минимальный воспроизводимый пример. Однако спасибо за подсказки, однако суть этого вопроса заключалась в сбое SwiftData, а не в архитектуре приложения и лучших практиках.
Поскольку это происходит на уровне основных данных/sql, вы можете включить ведение журнала отладки основных данных и посмотреть, покажет ли это что-нибудь интересное.
@Тимми Итак, что вы предлагаете, какую-то специальную кодировку для перечисления FrequencyType
?
@JoakimDanielson Я включил это, но мне это не помогло. Я могу разместить это здесь... Секундочку.
К сожалению, этот отрывок из журнала вообще не имеет значения, поскольку он относится к другой таблице.
@JoakimDanielson Это все, что я вижу в консоли, когда запускаю этот код :(
Тогда я предполагаю, что это внутренний сбой в Core Date перед попыткой сохранения данных. Посмотрим, смогу ли я воспроизвести это сам.
После исследования и отладки этой проблемы я пришел к выводу, что это происходит при генерации SQL, связанного со свойством, а также что кодирование свойства работает должным образом, поэтому я могу только предположить, что это ошибка/не поддерживается, поскольку нет вмешательства пользовательского кода. в процессе.
Обходным решением этой проблемы может быть превращение свойства перечисления в вычисляемое свойство, а затем использование структуры для частного свойства, которое сохраняется вместо перечисления. Это означает наличие дополнительного типа и некоторого связующего кода, но общедоступный API останется прежним, поэтому, когда эта проблема будет исправлена в SwiftData, нужно будет перенести только модель.
struct InternalFrequencyType: Codable {
let interval: Int?
let daysOfWeek: [DayOfWeek]?
init(frequencyType: FrequencyType) {
switch frequencyType {
case .daily(let interval):
self.interval = interval
daysOfWeek = nil
case .specificDaysOfWeek(let array):
daysOfWeek = array
interval = nil
}
}
var toFrequencyType: FrequencyType {
if let interval {
return .daily(interval: interval)
} else {
//alternatively to '?? []' use forced unwrapping or throw an error
return .specificDaysOfWeek(daysOfWeek ?? [])
}
}
}
Тогда изменения в модели будут
private var internalFrequencyType: InternalFrequencyType
var frequency: FrequencyType {
get { internalFrequencyType.toFrequencyType }
set { internalFrequencyType = .init(frequencyType: newValue) }
}
и в init
internalFrequencyType = .init(frequencyType: .daily(interval: 1))
Спасибо, это сработало так. И да, в момент разговора это кажется чем-то неподдерживаемым...
Кодировка
.specificDaysOfWeek([.friday, .monday])
дает{"specificDaysOfWeek":{"_0":[{"friday":{}},{"monday":{}}]}}
. Я предполагаю, что_0
, который вы видите в json, — это_buffer
.