Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation list performance #456

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let package = Package(
.package(url: "https://github.com/bufbuild/connect-swift", exact: "1.0.0"),
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.4.3"),
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", exact: "1.8.3"),
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.15")
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.18")
],
targets: [
.target(
Expand Down
9 changes: 9 additions & 0 deletions Sources/XMTPiOS/Conversation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ public enum Conversation: Identifiable, Equatable, Hashable {
return dm.id
}
}

public func lastMessage() async throws -> DecodedMessage? {
switch self {
case let .group(group):
return try await group.lastMessage()
case let .dm(dm):
return try await dm.lastMessage()
}
}

public func isCreator() async throws -> Bool {
switch self {
Expand Down
81 changes: 16 additions & 65 deletions Sources/XMTPiOS/Conversations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ public enum ConversationError: Error, CustomStringConvertible, LocalizedError {
}
}

public enum ConversationOrder {
case createdAt, lastMessage
}

public enum ConversationType {
case all, groups, dms
}
Expand Down Expand Up @@ -88,12 +84,12 @@ public actor Conversations {

public func listGroups(
createdAfter: Date? = nil, createdBefore: Date? = nil,
limit: Int? = nil, order: ConversationOrder = .createdAt,
limit: Int? = nil,
consentState: ConsentState? = nil
) async throws -> [Group] {
) throws -> [Group] {
var options = FfiListConversationsOptions(
createdAfterNs: nil, createdBeforeNs: nil, limit: nil,
consentState: consentState?.toFFI)
consentState: consentState?.toFFI, includeDuplicateDms: false)
if let createdAfter {
options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch)
}
Expand All @@ -104,25 +100,22 @@ public actor Conversations {
if let limit {
options.limit = Int64(limit)
}
let conversations = try await ffiConversations.listGroups(
let conversations = try ffiConversations.listGroups(
opts: options)

let sortedConversations = try await sortConversations(
conversations, order: order)

return sortedConversations.map {
return conversations.map {
$0.groupFromFFI(client: client)
}
}

public func listDms(
createdAfter: Date? = nil, createdBefore: Date? = nil,
limit: Int? = nil, order: ConversationOrder = .createdAt,
limit: Int? = nil,
consentState: ConsentState? = nil
) async throws -> [Dm] {
) throws -> [Dm] {
var options = FfiListConversationsOptions(
createdAfterNs: nil, createdBeforeNs: nil, limit: nil,
consentState: consentState?.toFFI)
consentState: consentState?.toFFI, includeDuplicateDms: false)
if let createdAfter {
options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch)
}
Expand All @@ -133,25 +126,22 @@ public actor Conversations {
if let limit {
options.limit = Int64(limit)
}
let conversations = try await ffiConversations.listDms(
let conversations = try ffiConversations.listDms(
opts: options)

let sortedConversations = try await sortConversations(
conversations, order: order)

return sortedConversations.map {
return conversations.map {
$0.dmFromFFI(client: client)
}
}

public func list(
createdAfter: Date? = nil, createdBefore: Date? = nil,
limit: Int? = nil, order: ConversationOrder = .createdAt,
limit: Int? = nil,
consentState: ConsentState? = nil
) async throws -> [Conversation] {
) throws -> [Conversation] {
var options = FfiListConversationsOptions(
createdAfterNs: nil, createdBeforeNs: nil, limit: nil,
consentState: consentState?.toFFI)
consentState: consentState?.toFFI, includeDuplicateDms: false)
if let createdAfter {
options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch)
}
Expand All @@ -162,50 +152,11 @@ public actor Conversations {
if let limit {
options.limit = Int64(limit)
}
let ffiConversations = try await ffiConversations.list(
let ffiConversations = try ffiConversations.list(
opts: options)

let sortedConversations = try await sortConversations(
ffiConversations, order: order)

var conversations: [Conversation] = []
for sortedConversation in sortedConversations {
let conversation = try await sortedConversation.toConversation(
client: client)
conversations.append(conversation)
}

return conversations
}

private func sortConversations(
_ conversations: [FfiConversation],
order: ConversationOrder
) async throws -> [FfiConversation] {
switch order {
case .lastMessage:
var conversationWithTimestamp: [(FfiConversation, Int64?)] = []

for conversation in conversations {
let message = try await conversation.findMessages(
opts: FfiListMessagesOptions(
sentBeforeNs: nil,
sentAfterNs: nil,
limit: 1,
deliveryStatus: nil,
direction: .descending
)
).first
conversationWithTimestamp.append(
(conversation, message?.sentAtNs))
}

let sortedTuples = conversationWithTimestamp.sorted { (lhs, rhs) in
(lhs.1 ?? 0) > (rhs.1 ?? 0)
}
return sortedTuples.map { $0.0 }
case .createdAt:
return conversations
return try ffiConversations.map {
try $0.toConversation(client: client)
}
}

Expand Down
16 changes: 14 additions & 2 deletions Sources/XMTPiOS/Dm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import LibXMTP

public struct Dm: Identifiable, Equatable, Hashable {
var ffiConversation: FfiConversation
var ffiLastMessage: FfiMessage? = nil
var client: Client
let streamHolder = StreamHolder()

Expand Down Expand Up @@ -200,6 +201,15 @@ public struct Dm: Identifiable, Equatable, Hashable {
}
}

public func lastMessage() async throws -> DecodedMessage? {
if let ffiMessage = ffiLastMessage {
return Message(client: self.client, ffiMessage: ffiMessage)
.decodeOrNull()
} else {
return try await messages(limit: 1).first
}
}

public func messages(
beforeNs: Int64? = nil,
afterNs: Int64? = nil,
Expand All @@ -212,7 +222,8 @@ public struct Dm: Identifiable, Equatable, Hashable {
sentAfterNs: nil,
limit: nil,
deliveryStatus: nil,
direction: nil
direction: nil,
contentTypes: nil
)

if let beforeNs {
Expand Down Expand Up @@ -253,7 +264,8 @@ public struct Dm: Identifiable, Equatable, Hashable {

options.direction = direction

return try await ffiConversation.findMessages(opts: options).compactMap {
return try await ffiConversation.findMessages(opts: options).compactMap
{
ffiMessage in
return Message(client: self.client, ffiMessage: ffiMessage)
.decodeOrNull()
Expand Down
18 changes: 18 additions & 0 deletions Sources/XMTPiOS/Extensions/Ffi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ extension FfiConversation {
}
}

extension FfiConversationListItem {
func groupFromFFI(client: Client) -> Group {
Group(ffiGroup: self.conversation(), ffiLastMessage: self.lastMessage(), client: client)
}

func dmFromFFI(client: Client) -> Dm {
Dm(ffiConversation: self.conversation(), ffiLastMessage: self.lastMessage(), client: client)
}

func toConversation(client: Client) throws -> Conversation {
if try conversation().dmPeerInboxId().isEmpty {
return Conversation.group(self.groupFromFFI(client: client))
} else {
return Conversation.dm(self.dmFromFFI(client: client))
}
}
}

extension FfiConversationMember {
var fromFFI: Member {
Member(ffiGroupMember: self)
Expand Down
13 changes: 12 additions & 1 deletion Sources/XMTPiOS/Group.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ final class StreamHolder {

public struct Group: Identifiable, Equatable, Hashable {
var ffiGroup: FfiConversation
var ffiLastMessage: FfiMessage? = nil
var client: Client
let streamHolder = StreamHolder()

Expand Down Expand Up @@ -398,6 +399,15 @@ public struct Group: Identifiable, Equatable, Hashable {
}
}

public func lastMessage() async throws -> DecodedMessage? {
if let ffiMessage = ffiLastMessage {
return Message(client: self.client, ffiMessage: ffiMessage)
.decodeOrNull()
} else {
return try await messages(limit: 1).first
}
}

public func messages(
beforeNs: Int64? = nil,
afterNs: Int64? = nil,
Expand All @@ -410,7 +420,8 @@ public struct Group: Identifiable, Equatable, Hashable {
sentAfterNs: nil,
limit: nil,
deliveryStatus: nil,
direction: nil
direction: nil,
contentTypes: nil
)

if let beforeNs {
Expand Down
9 changes: 1 addition & 8 deletions Tests/XMTPTests/ConversationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,10 @@ class ConversationTests: XCTestCase {

let conversations = try await fixtures.boClient.conversations
.list()
let conversationsOrdered = try await fixtures.boClient.conversations
.list(order: .lastMessage)

XCTAssertEqual(conversations.count, 3)
XCTAssertEqual(conversationsOrdered.count, 3)

XCTAssertEqual(
conversations.map { $0.id }, [dm.id, group1.id, group2.id])
XCTAssertEqual(
conversationsOrdered.map { $0.id },
[group2.id, dm.id, group1.id])
conversations.map { $0.id }, [group2.id, dm.id, group1.id])
}

func testCanStreamConversations() async throws {
Expand Down
10 changes: 1 addition & 9 deletions Tests/XMTPTests/DmTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,9 @@ class DmTests: XCTestCase {

let conversations = try await fixtures.boClient.conversations
.listDms()
let conversationsOrdered = try await fixtures.boClient.conversations
.listDms(order: .lastMessage)

XCTAssertEqual(conversations.count, 2)
XCTAssertEqual(conversationsOrdered.count, 2)

XCTAssertEqual(
try conversations.map { try $0.id }, [dm.id, dm2.id])
XCTAssertEqual(
try conversationsOrdered.map { try $0.id },
[dm2.id, dm.id])
try conversations.map { try $0.id }, [dm2.id, dm.id])
}

func testCanSendMessageToDm() async throws {
Expand Down
9 changes: 1 addition & 8 deletions Tests/XMTPTests/GroupTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,10 @@ class GroupTests: XCTestCase {

let conversations = try await fixtures.boClient.conversations
.listGroups()
let conversationsOrdered = try await fixtures.boClient.conversations
.listGroups(order: .lastMessage)

XCTAssertEqual(conversations.count, 2)
XCTAssertEqual(conversationsOrdered.count, 2)

XCTAssertEqual(
conversations.map { $0.id }, [group1.id, group2.id])
XCTAssertEqual(
conversationsOrdered.map { $0.id },
[group2.id, group1.id])
conversations.map { $0.id }, [group2.id, group1.id])
}

func testCanListGroupMembers() async throws {
Expand Down
2 changes: 1 addition & 1 deletion XMTP.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Pod::Spec.new do |spec|

spec.dependency 'CSecp256k1', '~> 0.2'
spec.dependency "Connect-Swift", "= 1.0.0"
spec.dependency 'LibXMTP', '= 3.0.15'
spec.dependency 'LibXMTP', '= 3.0.18'
spec.dependency 'CryptoSwift', '= 1.8.3'
spec.dependency 'SQLCipher', '= 4.5.7'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/xmtp/libxmtp-swift.git",
"state" : {
"revision" : "2fd0b26c9b3e20fcae7366176fbe41bc4528d0c7",
"version" : "3.0.15"
"revision" : "dc39e1b15013bbb8574d8ed7faa3ebebdd3b4863",
"version" : "3.0.18"
}
},
{
Expand Down
Loading