How to sort @ObservedResults for List

Hi,
I just learning about Swift and Realm, I’m following the Realm Database with SwiftUI QuickStart, which is really helpful.

But I was stacked here

@ObservedResults(Group.self) var groups

I know I can use sortDescriptor to sort the group, but I’d like to sort the groups.items by item.name, how can I implement with @ObservedResults?

I’m also trying let sortedGroup.items = group.items.sorted(byKeyPath: "name") . But the onDelete function won’t works since the IndexSet has changed.

I have the same issue, if I sort the onDelete feature doesn’t work. Without sort it works. Here is the code.

struct tempView: View {
    @ObservedRealmObject var user: User
    
    private let sortDescriptors = [
        SortDescriptor(keyPath: "name", ascending: true)
    ]
    
    var body: some View {
        
        VStack {
            // Works with no sorting
            //  if let activities = user.activties {
            
            // Delete doesn't work with sorting
            if let activities = user.activties.sorted(by: sortDescriptors) {
                List {
                    ForEach(activities) { activity in
                        ActivityRow(activity: activity.name)
                    }
                    .onDelete(perform: $user.activties.remove)
                }
            }
        }
    }
}

The same issue can be easily reproduced with the official example at realm-swift/examples/ios/swift/ListSwiftUI/.

Just replace the current implementation of ReminderListRowView with

struct ReminderListRowView: View {
    @ObservedRealmObject var list: ReminderList

    var body: some View {
        HStack {
            Image(systemName: list.icon)
            TextField("List Name", text: $list.name)
            Text(list.reminders.sorted(by: \Reminder.title, ascending: true).first?.title ?? "N/A")
            Spacer()
            Text("\(list.reminders.count)")
        }.frame(minWidth: 100)
    }
}

and then try to remove an added ReminderList from the launched example.

Of course it works perfectly when having list.reminders.first?.title ?? "N/A" (no sorting) :frowning:

Okay, I’ve got a workaround, but not sure about its performance/efficiency.

So to get sorted Results from a List the owner must be thawed first. In the ListSwiftUI example it can look like this:

class ReminderList: Object, ObjectKeyIdentifiable {
    @Persisted var name = "New List"
    @Persisted var icon: String = "list.bullet"
    @Persisted var reminders: RealmSwift.List<Reminder>
}

extension ReminderList {
    var sortedReminders: Results<Reminder>? {
        guard
            realm != nil,
            let thawed = thaw(),
            !thawed.isInvalidated
        else {
            return nil
        }
        
        return thawed.reminders.sorted(by: \Reminder.title, ascending: true)
    }
}

And later it can be used seamlessly:

struct ReminderListRowView: View {
    @ObservedRealmObject var list: ReminderList

    var body: some View {
        HStack {
            Image(systemName: list.icon)
            TextField("List Name", text: $list.name)
            Text(list.sortedReminders?.first?.title ?? "N/A")
            Spacer()
            Text("\(list.reminders.count)")
        }.frame(minWidth: 100)
    }
}
1 Like

I think to solve the issue you need to include an identifier in the ForEach header:

ForEach(items, id: \.self)

It seems pretty clear to me now that Realm’s SwiftUI support is experimental / prototype-level, and that many standard expectations of iOS interfaces (such as list sorting, optional selection objects, scene state restoration) require nasty workarounds if able to find one

The constructor ObservedResults can take a sort description for sorting.