iOS 18 SwiftData Bug: Codable Models Cause Relationship Mapping Error

Here we have yet another bug, I suppose, in SwiftData that happens on iOS18 but it is not an issue on iOS17.

There are 2 models defined as follows

@Model
final public class Note: Identifiable, Codable, Hashable
{
    public private(set) var uuid = UUID().uuidString
    var heading: String = ""
    var tags: [Tag]?

    init(heading: String = "") {
        self.heading = heading
    }

    required public init(from decoder: Decoder) throws {
        ...
    }

    public func encode(to encoder: Encoder) throws {
        ...
    }
}

@Model
final public class Tag: Identifiable, Codable
{
    var name: String = ""
    @Relationship(deleteRule: .nullify, inverse: \Note.tags) var notes: [Note]?

    init(_ name: String) {
        self.name = name
    }

    required public init(from decoder: Decoder) throws {
        …
    }

    public func encode(to encoder: Encoder) throws {
        ...
    }
 }

and a function o add new tags as follows

private func addTags(note: Note, tagNames: [String]) {
    if note.tags == nil {
        note.tags = []
    }
    for tagName in tagNames {
        if let tag = fetchTag(tagName) {
            if !note.tags!.contains(where: {$0.name == tagName}) {
                note.tags!.append(tag)
            }
        } else {
            // The following line throws the exception on iOS18 when Tag conforms to Codable:
            // Illegal attempt to map a relationship containing temporary objects to its identifiers.
            note.tags!.append(Tag(tagName))
        }
    }
}

This code works perfectly well on iOS17 but on iOS18 I get the exception “Illegal attempt to map a relationship containing temporary objects to its identifiers.”

What I noticed that this happens only when Tag model conforms to Codable protocol. Is it a bug? It looks like, otherwise we've got some undocumented changes have been made.

In my previous post I mentioned about the other issue about ModelContext that is broken too on iOS18 - I mean it works perfectly well on iOS17.

Demo app with an example how to workaround this problem is available here on GitHub.

Repro steps:

  • Add a note with some tags (separated by space)
  • Edit this note and add a new tag (tag that does not exists in database) and tap Save.

You should noticed that the tag hasn't been added. It works occasionally but hardly to be seen.

I do have exact same issue you described, works fine when creating new item and adding elements in the array, but if attempt to edit such item and remove or add elements I get exact same error.

Did you find a solution?

It seems to be fixed in iOS 18.1 but this bug cost me a lot. After I exported data, deleted all data in the app and restored all data from the backup file, almost all tags had been lost before I noticed that something was not on. Red card for SwiftData team!

iOS 18 SwiftData Bug: Codable Models Cause Relationship Mapping Error
 
 
Q