SwiftData does not persist change on relationship on iOS 18 beta 7

I came across of something I'm struggling to comprehend. I've got an iOS app based on SwiftUI and SwiftData + CloudKit. I wrote it using Xcode 15 and the target was iOS 17. Everything works fine in this environment, but after upgrading my phone to iOS 18 beta 7 something very strange started to happen with SwiftData on a physical device and in the simulator.

Every time when data is updated, to be precise - when the relationship is modified, the change is reverted after 15 seconds!

I've got the following settings on and nothing can be seen it's going on there in the logs

  • -com.apple.CoreData.Logging.stderr 1
  • -com.apple.CoreData.CloudKitDebug 1
  • -com.apple.CoreData.SQLDebug 1
  • -com.apple.CoreData.ConcurrencyDebug 1

Here you are some simplified code extraction:

@Model
final public class Note: Identifiable, Hashable
{
    public private(set) var uuid = UUID().uuidString
    var notification: Notification?
    ...
}

@Model
final public class Notification: Identifiable, Hashable
{
    var dateId: String = ""
    @Relationship(deleteRule: .nullify, inverse: \Note.notification) var notes: [Note]?

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

@ModelActor
final public actor DataModelActor : DataModel
{
    public func updateNotification(oldDate: Date, newDate: Date? = nil, persistentModelId: PersistentIdentifier) {
        if let note = modelContext.model(for: persistentModelId) as? Note {
            updateNotification(oldDate: oldDate, newDate: newDate, note: note)
        }
        try? self.modelContext.save()
    }

    private func updateNotification(oldDate: Date? = nil, newDate: Date? = nil, note: Note) {
        if let oldDate = oldDate {
            let notifications = fetchNotifications()
            let oldDateId = NotificationDateFactory.getId(from: oldDate)
            // removing the note from the collection related to oldDate
            if let notification = notifications.first(where: { $0.dateId == oldDateId }) {
                if let notificationNotes = notification.notes {
                    if let notificationNoteIndex = notification.notes!.firstIndex(of: note) {
                         notification.notes!.remove(at: notificationNoteIndex)
                    }
                    if notification.notes == nil || notification.notes!.isEmpty {
                        self.modelContext.delete(notification)
                    }
                }
            }
        }
        if let newDate = newDate, newDate > Calendar.current.startOfToday() {
            // adding to a new collection related to newDate
            let notifications = fetchNotifications()
            let newDateId = NotificationDateFactory.getId(from: newDate)
            if let notification = notifications.first(where: { $0.dateId == newDateId }) {
                note.notification = notification
            } else {
                let notification = Notification(newDateId)
                note.notification = notification
            }
        }
    }
}

Spreading save method here and there does not help :(

I've used Core Data Lab software to look into database and I can clearly see data changes are reverted for relationship property.

Example:

In Notification database there is one element:

2024-08-26 (3)

with 3 notes attached. I modified one note to send notification on 2024-08-27. Changes in database reflects situation correctly showing:

  • 2014-08-26 (2)
  • 2024-08-27 (1)

BUT!!! After 15 seconds doing noting database looks like this:

  • 2024-08-26 (3)
  • 2024-08-27 (0)

All changes were reverted and all notes are still attached to the same date as they were at the first place.

Any thoughts?

let notification = Notification(newDateId)
note.notification = notification

I believe you need to call modelContext.insert (and possibly save) on the new Notification before you set it as note.notification.

I've created an example app that replicates the issue or my wrong doing.

Follow this scenario for iOS17 and then iOS18 in the simulator.

  1. Add 3 notes
  2. Change review date for the second, and the third note.
  3. Switch immediately to Reviews view and remember results.
  4. Go back to Notes view
  5. Switch to Reviews view again after ~20-25 seconds

In iOS17 the view is unchanged.

In iOS18 the change is reverted.

When you inspect the database with Core Data Lab you can see that the relationship property has been rolled back.

Example code can be downloaded from here

I do really hope someone else can spot what I am doing wrong.

I have finally found the culprit. I'm using 2 model contexts at the same time to update the data.

More information and demo app here.

SwiftData does not persist change on relationship on iOS 18 beta 7
 
 
Q