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

Speed up build client performance #450

Merged
merged 4 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ try await conversation.send("gm")
// Listen for new messages in the conversation
Task {
for await message in try await conversation.streamMessages() {
print("\(message.senderAddress): \(message.body)")
print("\(message.senderInboxId): \(message.body)")
}
}
```
Expand Down Expand Up @@ -193,7 +193,7 @@ let nextPage = try await conversation.messages(limit: 25, before: messages.first

You can listen for any new messages (incoming or outgoing) in a conversation by calling `conversation.streamMessages()`.

A successfully received message (that makes it through the decoding and decryption without throwing) can be trusted to be authentic. Authentic means that it was sent by the owner of the `message.senderAddress` account and that it wasn't modified in transit. The `message.sent` timestamp can be trusted to have been set by the sender.
A successfully received message (that makes it through the decoding and decryption without throwing) can be trusted to be authentic. Authentic means that it was sent by the owner of the `message.senderInboxId` account and that it wasn't modified in transit. The `message.sent` timestamp can be trusted to have been set by the sender.

The flow returned by the `stream` methods is an asynchronous data stream that sequentially emits values and completes normally or with an exception.

Expand All @@ -202,10 +202,10 @@ The flow returned by the `stream` methods is an asynchronous data stream that se

Task {
for await message in try await conversation.streamMessages() {
if message.senderAddress == client.address {
if message.senderInboxId == client.address {
// This message was sent from me
}
print("New message from \(message.senderAddress): \(message.body)")
print("New message from \(message.senderInboxId): \(message.body)")
}
}
```
Expand Down
69 changes: 48 additions & 21 deletions Sources/XMTPiOS/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public final class Client {
public let dbPath: String
public let installationID: String
public let environment: XMTPEnvironment
public let apiClient: XmtpApiClient
private let ffiClient: LibXMTP.FfiXmtpClient

public lazy var conversations: Conversations = .init(
Expand All @@ -108,13 +109,15 @@ public final class Client {
accountAddress: String,
options: ClientOptions,
signingKey: SigningKey?,
inboxId: String
inboxId: String,
apiClient: XmtpApiClient? = nil
) async throws -> Client {
let (libxmtpClient, dbPath) = try await initFFiClient(
let (libxmtpClient, dbPath, apiClient) = try await initFFiClient(
accountAddress: accountAddress.lowercased(),
options: options,
signingKey: signingKey,
inboxId: inboxId
inboxId: inboxId,
apiClient: apiClient
)

let client = try Client(
Expand All @@ -123,7 +126,8 @@ public final class Client {
dbPath: dbPath,
installationID: libxmtpClient.installationId().toHex,
inboxID: libxmtpClient.inboxId(),
environment: options.api.env
environment: options.api.env,
apiClient: apiClient
)

// Register codecs
Expand All @@ -134,7 +138,10 @@ public final class Client {
return client
}

public static func create(account: SigningKey, options: ClientOptions)
public static func create(
account: SigningKey, options: ClientOptions,
apiClient: XmtpApiClient? = nil
)
async throws -> Client
{
let accountAddress = account.address.lowercased()
Expand All @@ -145,12 +152,14 @@ public final class Client {
accountAddress: accountAddress,
options: options,
signingKey: account,
inboxId: inboxId
inboxId: inboxId,
apiClient: apiClient
)
}

public static func build(
address: String, options: ClientOptions, inboxId: String? = nil
address: String, options: ClientOptions, inboxId: String? = nil,
apiClient: XmtpApiClient? = nil
)
async throws -> Client
{
Expand All @@ -167,16 +176,18 @@ public final class Client {
accountAddress: accountAddress,
options: options,
signingKey: nil,
inboxId: resolvedInboxId
inboxId: resolvedInboxId,
apiClient: apiClient
)
}

private static func initFFiClient(
accountAddress: String,
options: ClientOptions,
signingKey: SigningKey?,
inboxId: String
) async throws -> (FfiXmtpClient, String) {
inboxId: String,
apiClient: XmtpApiClient? = nil
) async throws -> (FfiXmtpClient, String, XmtpApiClient) {
let address = accountAddress.lowercased()

let mlsDbDirectory = options.dbDirectory
Expand All @@ -203,10 +214,15 @@ public final class Client {
let alias = "xmtp-\(options.api.env.rawValue)-\(inboxId).db3"
let dbURL = directoryURL.appendingPathComponent(alias).path

let xmtpApiClient: XmtpApiClient
if let existingApiClient = apiClient {
xmtpApiClient = existingApiClient
} else {
xmtpApiClient = try await connectToApiBackend(api: options.api)
}

let ffiClient = try await LibXMTP.createClient(
api: connectToBackend(
host: options.api.env.url,
isSecure: options.api.env.isSecure == true),
api: xmtpApiClient,
db: dbURL,
encryptionKey: options.dbEncryptionKey,
inboxId: inboxId,
Expand Down Expand Up @@ -236,7 +252,7 @@ public final class Client {
}
}

return (ffiClient, dbURL)
return (ffiClient, dbURL, xmtpApiClient)
}

private static func handleSignature(
Expand Down Expand Up @@ -266,6 +282,14 @@ public final class Client {
}
}

public static func connectToApiBackend(
api: ClientOptions.Api
) async throws -> XmtpApiClient {
return try await connectToBackend(
host: api.env.url,
isSecure: api.env.isSecure == true)
}

public static func getOrCreateInboxId(
api: ClientOptions.Api, address: String
) async throws -> String {
Expand Down Expand Up @@ -293,14 +317,12 @@ public final class Client {
let address = "0x0000000000000000000000000000000000000000"
let inboxId = try await getOrCreateInboxId(api: api, address: address)

var directoryURL: URL = URL.documentsDirectory
let directoryURL: URL = URL.documentsDirectory
let alias = "xmtp-\(api.env.rawValue)-\(inboxId).db3"
let dbURL = directoryURL.appendingPathComponent(alias).path

let ffiClient = try await LibXMTP.createClient(
api: connectToBackend(
host: api.env.url,
isSecure: api.env.isSecure == true),
api: connectToApiBackend(api: api),
db: dbURL,
encryptionKey: nil,
inboxId: inboxId,
Expand All @@ -322,14 +344,16 @@ public final class Client {

init(
address: String, ffiClient: LibXMTP.FfiXmtpClient, dbPath: String,
installationID: String, inboxID: String, environment: XMTPEnvironment
installationID: String, inboxID: String, environment: XMTPEnvironment,
apiClient: XmtpApiClient
) throws {
self.address = address
self.ffiClient = ffiClient
self.dbPath = dbPath
self.installationID = installationID
self.inboxID = inboxID
self.environment = environment
self.apiClient = apiClient
}

public func addAccount(newAccount: SigningKey)
Expand Down Expand Up @@ -450,7 +474,8 @@ public final class Client {
}
}

public func findConversation(conversationId: String) async throws -> Conversation?
public func findConversation(conversationId: String) async throws
-> Conversation?
{
do {
let conversation = try ffiClient.conversation(
Expand All @@ -461,7 +486,9 @@ public final class Client {
}
}

public func findConversationByTopic(topic: String) async throws -> Conversation? {
public func findConversationByTopic(topic: String) async throws
-> Conversation?
{
do {
let regexPattern = #"/xmtp/mls/1/g-(.*?)/proto"#
if let regex = try? NSRegularExpression(pattern: regexPattern) {
Expand Down
6 changes: 3 additions & 3 deletions Sources/XMTPiOS/DecodedMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public struct DecodedMessage: Sendable {
public var encodedContent: EncodedContent

/// The wallet address of the sender of the message
public var senderAddress: String
public var senderInboxId: String

/// When the message was sent
public var sent: Date
Expand All @@ -24,7 +24,7 @@ public struct DecodedMessage: Sendable {
client: Client,
topic: String,
encodedContent: EncodedContent,
senderAddress: String,
senderInboxId: String,
sent: Date,
sentNs: Int64,
deliveryStatus: MessageDeliveryStatus = .published
Expand All @@ -33,7 +33,7 @@ public struct DecodedMessage: Sendable {
self.client = client
self.topic = topic
self.encodedContent = encodedContent
self.senderAddress = senderAddress
self.senderInboxId = senderInboxId
self.sent = sent
self.sentNs = sentNs
self.deliveryStatus = deliveryStatus
Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTPiOS/Libxmtp/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public struct Message: Identifiable {
client: client,
topic: Topic.groupMessage(convoId).description,
encodedContent: encodedContent,
senderAddress: senderInboxId,
senderInboxId: senderInboxId,
sent: sentAt,
sentNs: sentAtNs,
deliveryStatus: deliveryStatus
Expand Down
31 changes: 31 additions & 0 deletions Tests/XMTPTests/ClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,21 @@ class ClientTests: XCTestCase {
let time3 = end3.timeIntervalSince(start3)
print("PERF: Built a client with inboxId in \(time3)s")

// Measure time to build a client with an inboxId and apiClient
let start4 = Date()
let buildClient3 = try await Client.build(
address: fakeWallet.address,
options: ClientOptions(
api: ClientOptions.Api(env: .dev, isSecure: true),
dbEncryptionKey: key
),
inboxId: client.inboxID,
apiClient: client.apiClient
)
let end4 = Date()
let time4 = end4.timeIntervalSince(start4)
print("PERF: Built a client with inboxId and apiClient in \(time4)s")

// Assert performance comparisons
XCTAssertTrue(
time2 < time1,
Expand All @@ -543,6 +558,18 @@ class ClientTests: XCTestCase {
time3 < time2,
"Building a client with inboxId should be faster than building one without."
)
XCTAssertTrue(
time4 < time1,
"Building a client with apiClient should be faster than creating one."
)
XCTAssertTrue(
time4 < time2,
"Building a client with apiClient should be faster than building one."
)
XCTAssertTrue(
time4 < time2,
"Building a client with apiClient should be faster than building one with inboxId."
)

// Assert that inbox IDs match
XCTAssertEqual(
Expand All @@ -553,6 +580,10 @@ class ClientTests: XCTestCase {
client.inboxID, buildClient2.inboxID,
"Inbox ID of the created client and second built client should match."
)
XCTAssertEqual(
client.inboxID, buildClient3.inboxID,
"Inbox ID of the created client and second built client should match."
)
}

}
2 changes: 1 addition & 1 deletion XMTP.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = "XMTP"
spec.version = "3.0.18"
spec.version = "3.0.19"

spec.summary = "XMTP SDK Cocoapod"

Expand Down
14 changes: 7 additions & 7 deletions XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ struct MessageTextView: View {
var body: some View {
VStack {
HStack {
if message.senderAddress.lowercased() == myAddress.lowercased() {
if message.senderInboxId.lowercased() == myAddress.lowercased() {
Spacer()
}
VStack(alignment: .leading) {
if isGroup && message.senderAddress.lowercased() != myAddress.lowercased() {
Text(message.senderAddress)
if isGroup && message.senderInboxId.lowercased() != myAddress.lowercased() {
Text(message.senderInboxId)
.font(.caption)
.foregroundStyle(.secondary)
}
Expand All @@ -32,7 +32,7 @@ struct MessageTextView: View {
if isDebugging {
Text("My Address \(myAddress)")
.font(.caption)
Text("Sender Address \(message.senderAddress)")
Text("Sender Address \(message.senderInboxId)")
.font(.caption)
}
}
Expand All @@ -46,7 +46,7 @@ struct MessageTextView: View {
isDebugging.toggle()
}
}
if message.senderAddress.lowercased() != myAddress.lowercased() {
if message.senderInboxId.lowercased() != myAddress.lowercased() {
Spacer()
}
}
Expand All @@ -62,15 +62,15 @@ struct MessageTextView: View {
}

var background: Color {
if message.senderAddress.lowercased() == myAddress.lowercased() {
if message.senderInboxId.lowercased() == myAddress.lowercased() {
return .purple
} else {
return .secondary.opacity(0.2)
}
}

var color: Color {
if message.senderAddress.lowercased() == myAddress.lowercased() {
if message.senderInboxId.lowercased() == myAddress.lowercased() {
return .white
} else {
return .primary
Expand Down
Loading
Loading