From 3fae8248f4a425583bb24cd152d373c9dd218ffa Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Thu, 19 Dec 2024 21:48:18 -0800 Subject: [PATCH 1/2] performance follow ups --- Package.swift | 2 +- Sources/XMTPiOS/Client.swift | 76 ++++++++++--------- XMTP.podspec | 4 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/Package.swift b/Package.swift index 46fe7a30..23d9b5ee 100644 --- a/Package.swift +++ b/Package.swift @@ -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.13") + .package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.14") ], targets: [ .target( diff --git a/Sources/XMTPiOS/Client.swift b/Sources/XMTPiOS/Client.swift index 70d6736f..b7b54728 100644 --- a/Sources/XMTPiOS/Client.swift +++ b/Sources/XMTPiOS/Client.swift @@ -84,6 +84,18 @@ public struct ClientOptions { } } +actor ApiClientCache { + private var apiClientCache: [String: XmtpApiClient] = [:] + + func getClient(forKey key: String) -> XmtpApiClient? { + return apiClientCache[key] + } + + func setClient(_ client: XmtpApiClient, forKey key: String) { + apiClientCache[key] = client + } +} + public final class Client { public let address: String public let inboxID: String @@ -91,8 +103,8 @@ 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 + private static let apiCache = ApiClientCache() public lazy var conversations: Conversations = .init( client: self, ffiConversations: ffiClient.conversations()) @@ -112,12 +124,11 @@ public final class Client { inboxId: String, apiClient: XmtpApiClient? = nil ) async throws -> Client { - let (libxmtpClient, dbPath, apiClient) = try await initFFiClient( + let (libxmtpClient, dbPath) = try await initFFiClient( accountAddress: accountAddress.lowercased(), options: options, signingKey: signingKey, - inboxId: inboxId, - apiClient: apiClient + inboxId: inboxId ) let client = try Client( @@ -126,8 +137,7 @@ public final class Client { dbPath: dbPath, installationID: libxmtpClient.installationId().toHex, inboxID: libxmtpClient.inboxId(), - environment: options.api.env, - apiClient: apiClient + environment: options.api.env ) // Register codecs @@ -139,8 +149,7 @@ public final class Client { } public static func create( - account: SigningKey, options: ClientOptions, - apiClient: XmtpApiClient? = nil + account: SigningKey, options: ClientOptions ) async throws -> Client { @@ -152,14 +161,12 @@ public final class Client { accountAddress: accountAddress, options: options, signingKey: account, - inboxId: inboxId, - apiClient: apiClient + inboxId: inboxId ) } public static func build( - address: String, options: ClientOptions, inboxId: String? = nil, - apiClient: XmtpApiClient? = nil + address: String, options: ClientOptions, inboxId: String? = nil ) async throws -> Client { @@ -176,8 +183,7 @@ public final class Client { accountAddress: accountAddress, options: options, signingKey: nil, - inboxId: resolvedInboxId, - apiClient: apiClient + inboxId: resolvedInboxId ) } @@ -185,9 +191,8 @@ public final class Client { accountAddress: String, options: ClientOptions, signingKey: SigningKey?, - inboxId: String, - apiClient: XmtpApiClient? = nil - ) async throws -> (FfiXmtpClient, String, XmtpApiClient) { + inboxId: String + ) async throws -> (FfiXmtpClient, String) { let address = accountAddress.lowercased() let mlsDbDirectory = options.dbDirectory @@ -214,15 +219,8 @@ 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: xmtpApiClient, + api: connectToApiBackend(api: options.api), db: dbURL, encryptionKey: options.dbEncryptionKey, inboxId: inboxId, @@ -252,7 +250,7 @@ public final class Client { } } - return (ffiClient, dbURL, xmtpApiClient) + return (ffiClient, dbURL) } private static func handleSignature( @@ -282,12 +280,19 @@ 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 connectToApiBackend(api: ClientOptions.Api) async throws + -> XmtpApiClient + { + let cacheKey = api.env.url + + if let cachedClient = await apiCache.getClient(forKey: cacheKey) { + return cachedClient + } + + let apiClient = try await connectToBackend( + host: api.env.url, isSecure: api.isSecure) + await apiCache.setClient(apiClient, forKey: cacheKey) + return apiClient } public static func getOrCreateInboxId( @@ -297,8 +302,7 @@ public final class Client { do { inboxId = try await getInboxIdForAddress( - host: api.env.url, - isSecure: api.env.isSecure == true, + api: connectToApiBackend(api: api), accountAddress: address.lowercased() ) ?? generateInboxId( @@ -344,8 +348,7 @@ public final class Client { init( address: String, ffiClient: LibXMTP.FfiXmtpClient, dbPath: String, - installationID: String, inboxID: String, environment: XMTPEnvironment, - apiClient: XmtpApiClient + installationID: String, inboxID: String, environment: XMTPEnvironment ) throws { self.address = address self.ffiClient = ffiClient @@ -353,7 +356,6 @@ public final class Client { self.installationID = installationID self.inboxID = inboxID self.environment = environment - self.apiClient = apiClient } public func addAccount(newAccount: SigningKey) diff --git a/XMTP.podspec b/XMTP.podspec index eb213ddf..621f8976 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "XMTP" - spec.version = "3.0.19" + spec.version = "3.0.20" spec.summary = "XMTP SDK Cocoapod" @@ -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.13' + spec.dependency 'LibXMTP', '= 3.0.14' spec.dependency 'CryptoSwift', '= 1.8.3' spec.dependency 'SQLCipher', '= 4.5.7' diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7b70aa78..1bca2503 100644 --- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/xmtp/libxmtp-swift.git", "state" : { - "revision" : "203fd6d67bb72e3114b273ce9bbddd6fc747d583", - "version" : "3.0.13" + "revision" : "9cf94acdea7d03008b1f130a9848c38ee6b711de", + "version" : "3.0.14" } }, { From 0a399b0aa01bd8c8fe201f1c75c8ebde29be3ebc Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Thu, 19 Dec 2024 21:56:59 -0800 Subject: [PATCH 2/2] write a test --- Tests/XMTPTests/ClientTests.swift | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/Tests/XMTPTests/ClientTests.swift b/Tests/XMTPTests/ClientTests.swift index 8127933c..2266571b 100644 --- a/Tests/XMTPTests/ClientTests.swift +++ b/Tests/XMTPTests/ClientTests.swift @@ -532,19 +532,18 @@ class ClientTests: XCTestCase { print("PERF: Built a client with inboxId in \(time3)s") // Measure time to build a client with an inboxId and apiClient + try await Client.connectToApiBackend(api: ClientOptions.Api(env: .dev, isSecure: true)) let start4 = Date() - let buildClient3 = try await Client.build( - address: fakeWallet.address, + try await Client.create( + account: fakeWallet, 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") + print("PERF: Create a client with prebuild in \(time4)s") // Assert performance comparisons XCTAssertTrue( @@ -560,15 +559,7 @@ class ClientTests: XCTestCase { ) 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." + "Creating a client with apiClient should be faster than creating one without." ) // Assert that inbox IDs match @@ -580,10 +571,6 @@ 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." - ) } }