Search code examples
iosswiftrealmcomputed-properties

Sorting Realm results collection on computed property?


I'm using Realm in my Swift iOS app and I have a model that I'm trying to add a computed property to. Here's what the class looks like:

class Conversation: Object {

    @objc dynamic var conversationId: String = generateConversationId()
    @objc dynamic var createdTime = Date()
    @objc dynamic var title: String = ""
    let messages = List<Message>()

    var lastUpdated: Date {
        if let newestMessage = self.messages.sorted(byKeyPath: "timestamp", ascending: false).first {
            return newestMessage.timestamp
        } else {
            return Date(timeIntervalSince1970: 0)
        }
    }        
}

As you can see, this object represents a conversation which has several properties one of which is a list of messages that belong to the conversation. The computed property that I've added (lastUpdated) should just return the timestamp of the most recent message in the messages list or, when the list is empty, it would return a default date value.

In one of my view controllers, I'm creating a results collection to access the Conversation objects like this:

var conversations: Results<Conversation> = realm.objects(Conversation.self)

This works and I can access the lastUpdated property on each Conversation as expected. However, I'd like to have this collection sorted by the lastUpdated property so I tried modifying my results collection like this:

var conversations: Results<Conversation> = realm.objects(Conversation.self).sorted(byKeyPath: "lastUpdated", ascending: true)

When I do this, the app throws the following exception:

Terminating app due to uncaught exception 'RLMException', reason: 'Cannot sort on key path 'lastUpdated': property 'Conversation.lastUpdated' does not exist.'

I think this might be happening because the lastUpdated computed property isn't persisted on the Conversation object (I would use Realm Studio to quickly verify this but my Realm is encrypted). I'm not explicitly ignoring this property but I assume this is the case because lastUpdated is not decorated with @objc dynamic like all of my other properties. I tried adding that, wiping my app install, recompiling and testing again but I still end up with the same exception.

Maybe it's not possible to persist a computed property (because, well, it's computed!) but is there another way to specify that my Conversation results collection should be sorted by this property?


Solution

  • As you suspect, you cannot persist a computed property and Realm's sorted(byKeyPath:,ascending:) method only works with persisted properties. There is no way to sort a Results instance based on a computed property while keeping the auto-updating Results.

    However, there is a workaround if you don't need the auto-updating nature of Results. You can use Swift's sorted method, which takes a closure as its sorting parameter, which will result in a return value of type [Conversation]:

    let conversations = realm.objects(Conversation.self).sorted(by: {$0.lastUpdated < $1.lastUpdated})