Signal SIGABRT on accessing values from SwiftData query

I work on an iOS app using SwiftUI and SwiftData. I added a computed property to one of my models - Parent - that uses relationship - array of Child models - data and I started getting strange problems. Let me start with models:

@Model
final class Parent {
    var name: String
    @Relationship(deleteRule: .cascade, inverse: \Child.parent)
    var children: [Child]? = []

    var streak: Int {
        // Yes, I know that's not optimal solution for such counter ;)
        guard let children = children?.sorted(using: SortDescriptor(\.date, order: .reverse)) else { return 0 }
        var date = Date.now
        let calendar = Calendar.current
        for (index, child) in children.enumerated() {
            if !calendar.isDate(child.date, inSameDayAs: date) {
                return index
            }
            date = calendar.date(byAdding: .day, value: -1, to: date) ?? .now
        }
        return children.count
    }

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

@Model
final class Child {
    var date: Date
    @Relationship(deleteRule: .nullify)
    var parent: Parent?

    init(date: Date, parent: Parent) {
        self.date = date
        self.parent = parent
    }
}

At first everything works as expected. The problem arises once I try to remove one of child from the parent instance. I remove the value from context and save changes without any problems, at least not ones that can be caught by do { } catch. But instead of refreshing UI I get an signal SIGABRT somewhere inside SwiftData internals that points to the line where I'm trying (inside View body) get a child from a Query:

struct LastSevenDaysButtons: View {
    @Environment(\.modelContext)
    private var modelContext
    @Query
    private var children: [Child]

    private let dates: [Date]
    private let parent: Parent

    init(for parent: Parent) {
        self.parent = parent

        var lastSevenDays = [Date]()
        let calendar = Calendar.current
        let firstDate = calendar.date(byAdding: .day, value: -6, to: calendar.startOfDay(for: .now)) ?? .now
        var date = firstDate
        while date <= .now {
            lastSevenDays.append(date)
            date = calendar.date(byAdding: .day, value: 1, to: date) ?? .now
        }
        dates = lastSevenDays

        let parentId = parent.persistentModelID
        _children = Query(
            filter: #Predicate {
                $0.parent?.persistentModelID == parentId && $0.date >= firstDate
            },
            sort: [SortDescriptor(\Child.date, order: .reverse)],
            animation: .default
        )
    }

    var body: some View {
        VStack {
            HStack(alignment: .top) {
                ForEach(dates, id: \.self) { date in
                    // Here is the last point on stack from my code that I see
                    let child = children.first { $0.date == date }
                    Button {
                        if let child {
                            modelContext.delete(child)
                        } else {
                            modelContext.insert(Child(date: date, parent: parent))
                        }
                        do {
                            try modelContext.save()
                        } catch {
                            print("Can't save changes for \(parent.name) on \(date.formatted(date: .abbreviated, time: .omitted)): \(error.localizedDescription)")
                        }
                    } label: {
                        Text("\(date.formatted(date: .abbreviated, time: .omitted))")
                            .foregroundStyle(child == nil ? .red : .blue)
                    }
                }
            }
        }
    }
}

The LastSevenDaysButtons View is kind of deep in a View hierarchy:

RootView -> ParentList -> ParentListItem -> LastSevenDaysButtons

However once I move insides of ParentList to RootView application works just fine, although I see and warning: === AttributeGraph: cycle detected through attribute 6912 ===.

What could be that I do wrong in here? I believe it must something I'm missing here, but after 2 days of debug, trial and errors, I can't think clearly anymore.

Here is the minimal repro I managed to create: Signal SIGABRT on accessing values from SwiftData query

Did you try with the latest public release OS version yet?

The crash happens when I run your code on my iOS 18.2.1 (22C161) device , but does not happen on my iOS 18.3 (22D63) device, and so it seems to me that the issue is a bug that has been fixed.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I think I found what causes the problem. I manually manage the context state, I use save and transaction methods on mainContext. I can't remember why I started doing so in the first place, but I had some strange problem when I was accessing newly created object and manual save helped in that case. Now I removed all manual operations and all crashes, warnings and errors disappeared. I still however don't understand why manual saves cause such strange behaviors.

I still however don't understand why manual saves cause such strange behaviors.

I don't see that manually saving the context, as shown in your code snippet, has anything wrong. Since the issue doesn't happen anymore on the latest public release, I believe that is a bug that has been fixed.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Signal SIGABRT on accessing values from SwiftData query
 
 
Q