protocol LinkedListNode {
associatedtype Node: LinkedListNode
var id: UUID { get }
var prev: Node? { get set }
var next: Node? { get set }
}
enum PropertyType: Codable {
case a(String)
case b(Float)
case c(Point)
}
struct Properties: Codable {
var id: UUID
var props: [PropertyType]
init(_ props: [PropertyType]) {
self.id = UUID()
self.props = props
}
init(_ props: PropertyType...) {
self.id = UUID()
self.props = props.reduce(into: []) { temp, prop in
temp.append(prop)
}
}
}
@Model
final class ItemProp: LinkedListNode {
var id: UUID
var val: Properties
init(_ vals: Properties) {
self.id = UUID()
self.val = vals
}
var prev: ItemProp? = nil
var next: ItemProp? = nil
}
So I have this model for Swift Data, that is intended to manage undo-redo state in a larger model, where the ItemProp model constitutes a linked list with a struct contains an enum that represent the last change in state, so that entire state copies are not required for every change made.
This compiles fine, throws zero errors, but crashes when trying to initialize model container with error description:
" Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil)
Process: ComplexDataModels [31588]
Path: <none>
Date/Time: 2025-02-15 01:27:46 +0000
Application Specific Information:
libswiftCore.dylib:
ComplexDataModels/ComplexDataModelsApp.swift:25: Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil)
dyld:
dyld config: DYLD_LIBRARY_PATH=/Users/shawn/Library/Developer/Xcode/DerivedData/ComplexDataModels-bspdzdqzdenkwfcvncohinchivnb/Build/Products/Debug DYLD_INSERT_LIBRARIES=@executable_path/__preview.dylib:/Applications/Xcode.app/Contents/Developer/usr/lib/libLogRedirect.dylib:/System/Library/PrivateFrameworks/LiveExecutionResultsProbe.framework/LiveExecutionResultsProbe:/System/Library/PrivateFrameworks/PreviewsInjection.framework/PreviewsInjection DYLD_FRAMEWORK_PATH=/Users/shawn/Library/Developer/Xcode/DerivedData/ComplexDataModels-bspdzdqzdenkwfcvncohinchivnb/Build/Products/Debug
Crashing Thread:
0 libswiftCore.dylib 0x0000380a4 _assertionFailure(_:_:file:line:flags:) + 268
1 ??? 0x3800145a8 _$s17ComplexDataModels0abC3AppV20sharedModelContainer05SwiftB00fG0CvpfiAGyXEfU_$12$body
2 ??? 0x380014e7c _$s17ComplexDataModels0abC3AppVACycfC$12$body
3 ??? 0x380014f20 _$s17ComplexDataModels0abC3AppV7SwiftUI0D0AadEPxycfCTW$12$body
4 SwiftUI 0x00083de58 static App.main() + 208
5 ??? 0x380014dd8 _$s17ComplexDataModels0abC3AppV5$mainyyFZ$12$body
6 ??? 0x380014f40 _main$12$body
7 ComplexDataModels 0x000002220 __debug_blank_executor_run_user_entry_point + 144
8 PreviewsInjection 0x000037588 ???
9 PreviewsInjection 0x0000382a0 ???
10 PreviewsInjection 0x000038164 __previews_injection_run_user_entrypoint + 16
11 XOJITExecutor 0x000007adc __xojit_executor_run_program_wrapper + 1832
12 XOJITExecutor 0x0000037cc ???
13 PreviewsInjection 0x000038098 ???
14 ComplexDataModels 0x000001960 __debug_blank_executor_main + 1056
15 dyld "
I am compiling for Swift 6
I was under the impression that enums with associated types especially when wrapped in codeable structs are just stored as blobs in the model, and all interactions with the enum takes place in a controller external to the model.
Im really not sure what the problem is as the error message doesn't give me any information I see as useful for identifying what is happening, or why it's crashing. Is this a bug, or am I doing something extremely wrong that the compiler is failing to notify me about?
i think so, but the stored type of each associated value is different (string, float, point) , so its just a guess but there might be a problem initializing the enums in the array...
what happens if you set the associated type to be string for every case?
Still fails to initialize the modelContainer.
I also tried with one single case in the enum, same thing.
It works without an associated property. Something about having an associated property is creating an issue, which it shouldn't especially wrapped in a codable struct that should just code to a blob.
Im extremely confused.
this might be because swift data has no idea how to encode/decode the associated values ( i know this happens with swift json decoding of enums with associated values)
so you might need to write a custom encode/decode for the associated types and manually encode additional metadata/properties such as type as a string and upon decode switch on that and attempt to decode the associated value there
something like
enum Something: Codable {
case a(Float)
case b(String)
private struct InternalRepresentation<T: Codable> {
// you could probably make this an string enum
var type: String
// holds the associated value
var payload: T
init(from: any Decoder) throws {
// get the keyed container and decode the type string first, then switch on it "float" or "string" then try decode the T based on that
}
}
init(from: any Decoder) throws {
// do a try? and decode the container for each T/String until you get a non-nil then set self = ... based on the result
}
func encode(to: any Encoder) throws {
// switch self and convert to InternalRepresentation (with appropriate type string) and encode it
}
}
you could accomplish this for the node type or the enum itself or a property wrapper (probably the enum itself might be better if possible)
I will try out your solution and let you know if it works.
However, I was under the impression that the purpose of the Codable protocol was to add macros for encode and decode for encodable decodable conformance.
If it was an exotic type and the compiler couldn't figure out it might not encode properly, thats one thing, but for it to fail on foundational type like a string or a float, is either a bug, or something that isn't supposed to work, but isn't documenting
Implementing the encoders and decoders yourself are what are required to conform to Encodable and Decodable, Codable is supposed to do that as a macro, so is it a bug in the Codable macro expansion?
Are we really at a point where codable doesn't work for foundational types that documentation ensures us are codeable, and we have to write the actual encodable an decodable protocols to make it work?
iirc encoding works but its the decoding part with array + divergent types that fails, so its needed to handle at least the decoding part (at least in swift 5.x)
one experiment i might offer is, take your model outside of swift data and use json encoder/decoder and if it works as you expect then the problem might be limited to swift data itself
1 Like
I should add that I added the struct wrapper because the enum itself did not work as a model input, so I figured if i codable enum was wrapped in a codable struct, it would eliminate any potential side effect from the enum would have.
Is there a specific order of coding in nested coding instances that might cause the enum to still cause problems (even though all of my reading says it shouldn't) when wrapped in a codable struct?
The structure works fine encoded to json or any other format, and I suppose I could json encode and input that way, but that seems like not how it should work.
The problem is with the model container initializing the models, it's not just crashing trying to load a model.
The program compiles fine and IMMEDIATELY crashes because it cant construct the models.
i dont know, but this piqued my interest so i made a sample playground app and it seemed to work fine, so my guess is there is an issue in swift data and not swift itself
import SwiftUI
struct ContentView: View {
@State var myDecoded: MyData?
@State var error: (any Error)?
@State var myDecodedDataJson: String?
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
Button("try decode", action: testDecoding)
if let error {
Text(String(describing: error))
}
if let myDecodedDataJson {
Text(verbatim: myDecodedDataJson)
}
if let myDecoded {
Text(String(describing: myDecoded.myThings))
}
}
}
func testDecoding() {
let result = encoded
switch result {
case .success(let success):
myDecodedDataJson = String(
data: success,
encoding: .utf8
)
do {
let decoder = JSONDecoder()
decoder.assumesTopLevelDictionary = true
let decoded = try decoder.decode(
MyData.self,
from: success
)
self.myDecoded = decoded
} catch {
self.error = error
}
case .failure(let failure):
error = failure
}
}
}
enum Something: Codable {
case a( String)
case b( Int)
}
struct MyData: Codable {
var myThings: [Something] = []
}
let myData = MyData(myThings: [
.a("hello"),
.b( 1),
.a("world")
])
let encoded: Result<Data, Error> = {
let result: Result<Data, Error>
do {
let data = try JSONEncoder().encode(myData)
result = .success(data)
} catch {
result = .failure(error)
}
return result
}()
If thats true and it's a bug, it seems like something that should have been addressed. Using a enum with attributes as typeing is well established swift practice.
enum ValidatedUer {
.success(User)
.error(Error)
}
func loginUser(user: ValidatedUser) {
....
}
It's a very common pattern to provide a case wrapper that defines the result type of an operation, or am I mistaken?
it might very well be a bug in swift data which is an apple framework so it might be better to go over to apple forums and see if anyone has encountered same thing
fwiw using pure swift/json decoding/encoding those enums recursively as class works too in my sample app, so unless i understand incorrectly your example, its hard to say its a swift issue....
class MyData: Codable {
var myThings: [Something] = []
var another: MyData?
init(
myThings: [Something] = [],
another: MyData?
) {
self.myThings = myThings
self.another = another
}
}
let myData = MyData(
myThings: [
.a("hello"),
.b( 1),
.a("world")
],
another: .init(
myThings: [.a("another")],
another: nil
)
)
Thank you for confirmation. So we are in agreement that we should expect this to be a working valid SwiftData model and code, given the reading of how swift data should turn this into a blob, and how it definitely is valid swift?
Is there any repo specifically for swift data issues, if this is a swift data issue.
i dont think its open source, so the best i could suggest is apple dev forums