From 316e6b1cb6055863eb5bc79e250e260859dcd506 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 1 Apr 2024 16:10:05 -0700 Subject: [PATCH] Group Push Notifications (#301) * bump all the pods * add the mls topics * add group decryption for pushes * tweak the returning of topic of id * write a test --- Package.swift | 2 +- Sources/XMTPiOS/Client.swift | 8 +++++--- Sources/XMTPiOS/Conversation.swift | 2 +- Sources/XMTPiOS/Conversations.swift | 9 +++++++++ Sources/XMTPiOS/Extensions/Ffi.swift | 4 ++-- Sources/XMTPiOS/Group.swift | 14 ++++++++++++++ Sources/XMTPiOS/Messages/Topic.swift | 12 +++++++++++- Tests/XMTPTests/ClientTests.swift | 1 + XMTP.podspec | 4 ++-- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 10 files changed, 48 insertions(+), 12 deletions(-) diff --git a/Package.swift b/Package.swift index b7562c5f..a4538c6d 100644 --- a/Package.swift +++ b/Package.swift @@ -25,7 +25,7 @@ let package = Package( .package(url: "https://github.com/1024jp/GzipSwift", from: "5.2.0"), .package(url: "https://github.com/bufbuild/connect-swift", exact: "0.12.0"), .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"), - .package(url: "https://github.com/xmtp/libxmtp-swift", exact: "0.4.3-beta3"), + .package(url: "https://github.com/xmtp/libxmtp-swift", exact: "0.4.3-beta4"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/Sources/XMTPiOS/Client.swift b/Sources/XMTPiOS/Client.swift index 2badc6d4..1bbfaf20 100644 --- a/Sources/XMTPiOS/Client.swift +++ b/Sources/XMTPiOS/Client.swift @@ -90,6 +90,7 @@ public final class Client { let v3Client: LibXMTP.FfiXmtpClient? public let libXMTPVersion: String = getVersionInfo() let dbPath: String + public let installationID: String /// Access ``Conversations`` for this Client. public lazy var conversations: Conversations = .init(client: self) @@ -180,7 +181,7 @@ public final class Client { signingKey: account ) - let client = try Client(address: account.address, privateKeyBundleV1: privateKeyBundleV1, apiClient: apiClient, v3Client: v3Client, dbPath: dbPath) + let client = try Client(address: account.address, privateKeyBundleV1: privateKeyBundleV1, apiClient: apiClient, v3Client: v3Client, dbPath: dbPath, installationID: v3Client?.installationId().toHex ?? "") try await client.ensureUserContactPublished() for codec in (options?.codecs ?? []) { @@ -279,7 +280,7 @@ public final class Client { rustClient: client ) - let result = try Client(address: address, privateKeyBundleV1: v1Bundle, apiClient: apiClient, v3Client: v3Client, dbPath: dbPath) + let result = try Client(address: address, privateKeyBundleV1: v1Bundle, apiClient: apiClient, v3Client: v3Client, dbPath: dbPath, installationID: v3Client?.installationId().toHex ?? "") for codec in options.codecs { result.register(codec: codec) @@ -288,12 +289,13 @@ public final class Client { return result } - init(address: String, privateKeyBundleV1: PrivateKeyBundleV1, apiClient: ApiClient, v3Client: LibXMTP.FfiXmtpClient?, dbPath: String = "") throws { + init(address: String, privateKeyBundleV1: PrivateKeyBundleV1, apiClient: ApiClient, v3Client: LibXMTP.FfiXmtpClient?, dbPath: String = "", installationID: String) throws { self.address = address self.privateKeyBundleV1 = privateKeyBundleV1 self.apiClient = apiClient self.v3Client = v3Client self.dbPath = dbPath + self.installationID = installationID } public var privateKeyBundle: PrivateKeyBundle { diff --git a/Sources/XMTPiOS/Conversation.swift b/Sources/XMTPiOS/Conversation.swift index 099de748..85649f8d 100644 --- a/Sources/XMTPiOS/Conversation.swift +++ b/Sources/XMTPiOS/Conversation.swift @@ -260,7 +260,7 @@ public enum Conversation: Sendable { case let .v2(conversation): return conversation.topic case let .group(group): - return group.id.toHex + return group.topic } } diff --git a/Sources/XMTPiOS/Conversations.swift b/Sources/XMTPiOS/Conversations.swift index 7184ef6a..1d0980fb 100644 --- a/Sources/XMTPiOS/Conversations.swift +++ b/Sources/XMTPiOS/Conversations.swift @@ -437,6 +437,15 @@ public actor Conversations { (($0.value.conversationID ?? "") == (conversationID ?? "")) })?.value } + + public func fromWelcome(envelopeBytes: Data) throws -> Group? { + guard let v3Client = client.v3Client else { + return nil + } + + let group = try v3Client.conversations().processStreamedWelcomeMessage(envelopeBytes: envelopeBytes) + return Group(ffiGroup: group, client: client) + } public func newConversation(with peerAddress: String, context: InvitationV1.Context? = nil) async throws -> Conversation { if peerAddress.lowercased() == client.address.lowercased() { diff --git a/Sources/XMTPiOS/Extensions/Ffi.swift b/Sources/XMTPiOS/Extensions/Ffi.swift index 38668a08..3a32d4a6 100644 --- a/Sources/XMTPiOS/Extensions/Ffi.swift +++ b/Sources/XMTPiOS/Extensions/Ffi.swift @@ -201,7 +201,7 @@ extension FfiMessage { return DecodedMessage( client: client, - topic: convoId.toHex, + topic: Topic.groupMessage(convoId.toHex).description, encodedContent: encodedContent, senderAddress: addrFrom, sent: Date(timeIntervalSince1970: TimeInterval(sentAtNs / 1_000_000_000)) @@ -216,7 +216,7 @@ extension FfiMessage { encodedContent: encodedContent, senderAddress: addrFrom, sentAt: Date(timeIntervalSince1970: TimeInterval(sentAtNs / 1_000_000_000)), - topic: convoId.toHex + topic: Topic.groupMessage(convoId.toHex).description ) } } diff --git a/Sources/XMTPiOS/Group.swift b/Sources/XMTPiOS/Group.swift index d077bc7f..2c6715ed 100644 --- a/Sources/XMTPiOS/Group.swift +++ b/Sources/XMTPiOS/Group.swift @@ -42,6 +42,10 @@ public struct Group: Identifiable, Equatable, Hashable { public var id: Data { ffiGroup.id() } + + public var topic: String { + Topic.groupMessage(id.toHex).description + } func metadata() throws -> FfiGroupMetadata { return try ffiGroup.groupMetadata() @@ -102,6 +106,16 @@ public struct Group: Identifiable, Equatable, Hashable { public func removeMembers(addresses: [String]) async throws { try await ffiGroup.removeMembers(accountAddresses: addresses) } + + public func processMessage(envelopeBytes: Data) async throws -> DecodedMessage { + let message = try await ffiGroup.processStreamedGroupMessage(envelopeBytes: envelopeBytes) + return try message.fromFFI(client: client) + } + + public func processMessageDecrypted(envelopeBytes: Data) async throws -> DecryptedMessage { + let message = try await ffiGroup.processStreamedGroupMessage(envelopeBytes: envelopeBytes) + return try message.fromFFIDecrypted(client: client) + } public func send(content: T, options: SendOptions? = nil) async throws -> String { let preparedMessage = try await prepareMessage(content: content, options: options) diff --git a/Sources/XMTPiOS/Messages/Topic.swift b/Sources/XMTPiOS/Messages/Topic.swift index a3942d1f..f102f1ca 100644 --- a/Sources/XMTPiOS/Messages/Topic.swift +++ b/Sources/XMTPiOS/Messages/Topic.swift @@ -12,7 +12,9 @@ public enum Topic { userInvite(String), directMessageV1(String, String), directMessageV2(String), - preferenceList(String) + preferenceList(String), + userWelcome(String), + groupMessage(String) var description: String { switch self { @@ -31,12 +33,20 @@ public enum Topic { return wrap("m-\(randomString)") case let .preferenceList(identifier): return wrap("userpreferences-\(identifier)") + case let .groupMessage(groupId): + return wrapMls("g-\(groupId)") + case let .userWelcome(installationId): + return wrapMls("w-\(installationId)") } } private func wrap(_ value: String) -> String { "/xmtp/0/\(value)/proto" } + + private func wrapMls(_ value: String) -> String { + "/xmtp/mls/1/\(value)/proto" + } static func isValidTopic(topic: String) -> Bool { return topic.allSatisfy(\.isASCII) diff --git a/Tests/XMTPTests/ClientTests.swift b/Tests/XMTPTests/ClientTests.swift index b6424688..72d4f933 100644 --- a/Tests/XMTPTests/ClientTests.swift +++ b/Tests/XMTPTests/ClientTests.swift @@ -128,6 +128,7 @@ class ClientTests: XCTestCase { ) XCTAssertEqual(client.address, bundleClient.address) + XCTAssert(!client.installationID.isEmpty) await assertThrowsAsyncError( _ = try await Client.from( diff --git a/XMTP.podspec b/XMTP.podspec index 25453d4b..aea4614c 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "XMTP" - spec.version = "0.9.3" + spec.version = "0.9.4" spec.summary = "XMTP SDK Cocoapod" # This description is used to generate tags and improve search results. @@ -44,5 +44,5 @@ Pod::Spec.new do |spec| spec.dependency "web3.swift" spec.dependency "GzipSwift" spec.dependency "Connect-Swift", "= 0.12.0" - spec.dependency 'LibXMTP', '= 0.4.3-beta3' + spec.dependency 'LibXMTP', '= 0.4.3-beta4' end diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 817df3f3..7fccbae1 100644 --- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/xmtp/libxmtp-swift", "state" : { - "revision" : "b129e203957b690cbab91c165af90ddb5fa8bdb5", - "version" : "0.4.3-beta3" + "revision" : "fa57437cc1a84df566c91bbd5df1d0619d404e09", + "version" : "0.4.3-beta4" } }, {