The OP requested a Swift solution so I have two: which one is used depends on the data set and coding preference for relationships. There is no automatic inverse relationship in the question but wanted to include that just in case.
1 - Without LinkingObjects: a manual inverse relationship
Let's set up the models with a 1-Many relationship from a channel to messages and then a single inverse relationship from a message back to it's parent channel
class ChannelClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var channelName = ""
@Persisted var messageList: List<MessageClass>
}
class MessageClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var msg = ""
@Persisted var msgDate = ""
@Persisted var myChannel: ChannelClass!
}
Then after we populate Realm we have some objects that look like this - keeping in mind that different channels will have had messages added at different times
channel 0
message 1
message 3
message 4
channel 1
message 5
message 9
message 10
channel 2
message 2
message 6
message 7
message 8
It would look like this because: suppose a user posts a message to channel 0, which would be message 1. Then a day later another user posts a message to to channel 2, which would be message 2. Then, on another day, a user posts a message to channel 0 which would be message 3. Etc etc
Keeping in mind that while Realm objects are unsorted, List objects always maintain their order. So the last element in each channels list is the most current message in that channel.
From there getting the channels sorted by their most recent message is a one liner
let messages = realm.objects(MessageClass.self).sorted(byKeyPath: "msgDate").distinct(by: ["myChannel._id"])
If you now iterate over messages to print the channels, here's the output. Note: This is ONLY for showing the data has already been retrieved from Realm and would not be needed in an app.
Channel 0 //the first message
Channel 2 //the second message
Channel 1 //the third message? Not quite
Then you say to yourself "self, wait a sec - the third message was in channel 0! So why output channel 1 as the last item?"
The reasoning is that the OP has a requirement that channels should only be listed once - therefore since channel 0 was already listed, the remaining channel is channel 1.
2 - With LinkingObjects: Automatic inverse relationship
Take a scenario where LinkingObjects are used to automatically create the backlink from the messages object back to the channel e.g. reverse transversing the object graph
class MessageClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var msg = ""
@Persisted var msgDate = ""
@Persisted(originProperty: "messageList") var linkedChannels: LinkingObjects<ChannelClass>
}
the thought process is similar but we have to lean on Swift a little to provide a sort. Here's the one liner
let channels = realm.objects(ChannelClass.self).sorted { $0.messageList.last!.msgDate < $1.messageList.last!.msgDate }
What were doing here is querying the channels and using the msgDate property from the last message object in each channels list to sort the channels. and the output is the same
Channel 0 //the first message
Channel 2 //the second message
Channel 1 //see above
The only downside here is this solution will have larger memory impact but adds the convenience of automatic reverse relationships through LinkingObjects
3 Another option
Another options to add a small function and a property to the Channel class that both adds a message to the channels messagesList
and also populates the 'lastMsgDate' property of the channel. Then sorting the channels is a snap. So it would look like this
class ChannelClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var channelName = ""
@Persisted var messageList: List<MessageClass>
@Persisted var lastMsgDate: String
func addMessage(msg: MessageClass) {
self.messageList.append(msg)
self.lastMsgDate = msg.msgDate
}
}
When ever a message is added to the channel, the last message date is updated. Then sort channels by lastMsgDate
someChannel.addMessage(msg: someMessage)
Note: I used Strings for message dates for simplicity. If you want to do that ensure it's a yyyymmddhhmmss format, or just use an actual Date property.