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

1 Like

The constructor ObservedResults can take a sort description for sorting.