From 25e45d18b1b962231eb7a7bf469d47bb094b4842 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Thu, 26 Sep 2024 07:30:30 -0600 Subject: [PATCH] Expose timestamp on installations and make members async (#404) * update package * add installation timestamps and async members * fix up the tests and bump the pod --- Package.swift | 2 +- Sources/XMTPiOS/Conversation.swift | 4 +- Sources/XMTPiOS/Group.swift | 8 +-- Sources/XMTPiOS/Mls/InboxState.swift | 4 +- Sources/XMTPiOS/Mls/Installation.swift | 27 ++++++++ Tests/XMTPTests/ClientTests.swift | 5 +- Tests/XMTPTests/GroupPermissionsTests.swift | 6 +- Tests/XMTPTests/GroupTests.swift | 66 +++++++++++-------- Tests/XMTPTests/V3ClientTests.swift | 6 +- XMTP.podspec | 4 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- 11 files changed, 89 insertions(+), 47 deletions(-) create mode 100644 Sources/XMTPiOS/Mls/Installation.swift diff --git a/Package.swift b/Package.swift index f3431147..078c325f 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.git", exact: "0.5.8-beta6"), + .package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "0.5.8-beta7"), ], 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/Conversation.swift b/Sources/XMTPiOS/Conversation.swift index 807a5701..737bb1f2 100644 --- a/Sources/XMTPiOS/Conversation.swift +++ b/Sources/XMTPiOS/Conversation.swift @@ -83,7 +83,7 @@ public enum Conversation: Sendable { case let .v2(conversationV2): return conversationV2.peerAddress case let .group(group): - return try group.peerInboxIds.joined(separator: ",") + throw GroupError.notSupportedByGroups } } } @@ -96,7 +96,7 @@ public enum Conversation: Sendable { case let .v2(conversationV2): return [conversationV2.peerAddress] case let .group(group): - return try group.peerInboxIds + throw GroupError.notSupportedByGroups } } } diff --git a/Sources/XMTPiOS/Group.swift b/Sources/XMTPiOS/Group.swift index 05446058..b9ee9d24 100644 --- a/Sources/XMTPiOS/Group.swift +++ b/Sources/XMTPiOS/Group.swift @@ -112,16 +112,16 @@ public struct Group: Identifiable, Equatable, Hashable { } public var members: [Member] { - get throws { - return try ffiGroup.listMembers().map { ffiGroupMember in + get async throws { + return try await ffiGroup.listMembers().map { ffiGroupMember in Member(ffiGroupMember: ffiGroupMember) } } } public var peerInboxIds: [String] { - get throws { - var ids = try members.map(\.inboxId) + get async throws { + var ids = try await members.map(\.inboxId) if let index = ids.firstIndex(of: client.inboxID) { ids.remove(at: index) } diff --git a/Sources/XMTPiOS/Mls/InboxState.swift b/Sources/XMTPiOS/Mls/InboxState.swift index f604f15c..bad69551 100644 --- a/Sources/XMTPiOS/Mls/InboxState.swift +++ b/Sources/XMTPiOS/Mls/InboxState.swift @@ -23,8 +23,8 @@ public struct InboxState { ffiInboxState.accountAddresses } - public var installationIds: [String] { - ffiInboxState.installationIds.map { $0.toHex } + public var installations: [Installation] { + ffiInboxState.installations.map { Installation(ffiInstallation: $0) } } public var recoveryAddress: String { diff --git a/Sources/XMTPiOS/Mls/Installation.swift b/Sources/XMTPiOS/Mls/Installation.swift new file mode 100644 index 00000000..fa237ee0 --- /dev/null +++ b/Sources/XMTPiOS/Mls/Installation.swift @@ -0,0 +1,27 @@ +// +// Installation.swift +// +// +// Created by Naomi Plasterer on 9/25/24. +// + +import Foundation +import LibXMTP + +public struct Installation { + var ffiInstallation: FfiInstallation + + init(ffiInstallation: FfiInstallation) { + self.ffiInstallation = ffiInstallation + } + + public var id: String { + ffiInstallation.id.toHex + } + + var createdAt: Date? { + guard let timestampNs = ffiInstallation.clientTimestampNs else { return nil } + return Date(timeIntervalSince1970: TimeInterval(timestampNs) / 1_000_000_000) + } +} + diff --git a/Tests/XMTPTests/ClientTests.swift b/Tests/XMTPTests/ClientTests.swift index 234e6b7d..40b2495e 100644 --- a/Tests/XMTPTests/ClientTests.swift +++ b/Tests/XMTPTests/ClientTests.swift @@ -506,11 +506,12 @@ class ClientTests: XCTestCase { ) let state = try await alixClient3.inboxState(refreshFromNetwork: true) - XCTAssertEqual(state.installationIds.count, 3) + XCTAssertEqual(state.installations.count, 3) + XCTAssert(state.installations.first?.createdAt != nil) try await alixClient3.revokeAllOtherInstallations(signingKey: alix) let newState = try await alixClient3.inboxState(refreshFromNetwork: true) - XCTAssertEqual(newState.installationIds.count, 1) + XCTAssertEqual(newState.installations.count, 1) } } diff --git a/Tests/XMTPTests/GroupPermissionsTests.swift b/Tests/XMTPTests/GroupPermissionsTests.swift index d1293a91..8f362d6f 100644 --- a/Tests/XMTPTests/GroupPermissionsTests.swift +++ b/Tests/XMTPTests/GroupPermissionsTests.swift @@ -213,7 +213,7 @@ class GroupPermissionTests: XCTestCase { let aliceGroup = try await fixtures.aliceClient.conversations.groups().first! // Initial checks for group members and their permissions - var members = try bobGroup.members + var members = try await bobGroup.members var admins = members.filter { $0.permissionLevel == PermissionLevel.Admin } var superAdmins = members.filter { $0.permissionLevel == PermissionLevel.SuperAdmin } var regularMembers = members.filter { $0.permissionLevel == PermissionLevel.Member } @@ -227,7 +227,7 @@ class GroupPermissionTests: XCTestCase { try await bobGroup.sync() try await aliceGroup.sync() - members = try bobGroup.members + members = try await bobGroup.members admins = members.filter { $0.permissionLevel == PermissionLevel.Admin } superAdmins = members.filter { $0.permissionLevel == PermissionLevel.SuperAdmin } regularMembers = members.filter { $0.permissionLevel == PermissionLevel.Member } @@ -241,7 +241,7 @@ class GroupPermissionTests: XCTestCase { try await bobGroup.sync() try await aliceGroup.sync() - members = try bobGroup.members + members = try await bobGroup.members admins = members.filter { $0.permissionLevel == PermissionLevel.Admin } superAdmins = members.filter { $0.permissionLevel == PermissionLevel.SuperAdmin } regularMembers = members.filter { $0.permissionLevel == PermissionLevel.Member } diff --git a/Tests/XMTPTests/GroupTests.swift b/Tests/XMTPTests/GroupTests.swift index 1d4d25e7..6dfb9c61 100644 --- a/Tests/XMTPTests/GroupTests.swift +++ b/Tests/XMTPTests/GroupTests.swift @@ -98,16 +98,20 @@ class GroupTests: XCTestCase { try await aliceGroup.addMembers(addresses: [fixtures.fred.address]) try await bobGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 3) - XCTAssertEqual(try bobGroup.members.count, 3) + var aliceMembersCount = try await aliceGroup.members.count + var bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 3) + XCTAssertEqual(bobMembersCount, 3) try await bobGroup.addAdmin(inboxId: fixtures.aliceClient.inboxID) try await aliceGroup.removeMembers(addresses: [fixtures.fred.address]) try await bobGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 2) - XCTAssertEqual(try bobGroup.members.count, 2) + aliceMembersCount = try await aliceGroup.members.count + bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 2) + XCTAssertEqual(bobMembersCount, 2) try await bobGroup.addMembers(addresses: [fixtures.fred.address]) try await aliceGroup.sync() @@ -115,8 +119,10 @@ class GroupTests: XCTestCase { try await bobGroup.removeAdmin(inboxId: fixtures.aliceClient.inboxID) try await aliceGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 3) - XCTAssertEqual(try bobGroup.members.count, 3) + aliceMembersCount = try await aliceGroup.members.count + bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 3) + XCTAssertEqual(bobMembersCount, 3) XCTAssertEqual(try bobGroup.permissionPolicySet().addMemberPolicy, .allow) XCTAssertEqual(try aliceGroup.permissionPolicySet().addMemberPolicy, .allow) @@ -145,30 +151,38 @@ class GroupTests: XCTestCase { try await bobGroup.addMembers(addresses: [fixtures.fred.address]) try await aliceGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 3) - XCTAssertEqual(try bobGroup.members.count, 3) + var aliceMembersCount = try await aliceGroup.members.count + var bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 3) + XCTAssertEqual(bobMembersCount, 3) await assertThrowsAsyncError( try await aliceGroup.removeMembers(addresses: [fixtures.fred.address]) ) try await bobGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 3) - XCTAssertEqual(try bobGroup.members.count, 3) + aliceMembersCount = try await aliceGroup.members.count + bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 3) + XCTAssertEqual(bobMembersCount, 3) try await bobGroup.removeMembers(addresses: [fixtures.fred.address]) try await aliceGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 2) - XCTAssertEqual(try bobGroup.members.count, 2) + aliceMembersCount = try await aliceGroup.members.count + bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 2) + XCTAssertEqual(bobMembersCount, 2) await assertThrowsAsyncError( try await aliceGroup.addMembers(addresses: [fixtures.fred.address]) ) try await bobGroup.sync() - XCTAssertEqual(try aliceGroup.members.count, 2) - XCTAssertEqual(try bobGroup.members.count, 2) + aliceMembersCount = try await aliceGroup.members.count + bobMembersCount = try await bobGroup.members.count + XCTAssertEqual(aliceMembersCount, 2) + XCTAssertEqual(bobMembersCount, 2) XCTAssertEqual(try bobGroup.permissionPolicySet().addMemberPolicy, .admin) XCTAssertEqual(try aliceGroup.permissionPolicySet().addMemberPolicy, .admin) @@ -210,7 +224,7 @@ class GroupTests: XCTestCase { let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address]) try await group.sync() - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() let peerMembers = try Conversation.group(group).peerAddresses.sorted() XCTAssertEqual([fixtures.bobClient.inboxID, fixtures.aliceClient.inboxID].sorted(), members) @@ -224,7 +238,7 @@ class GroupTests: XCTestCase { try await group.addMembers(addresses: [fixtures.fred.address]) try await group.sync() - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, @@ -243,7 +257,7 @@ class GroupTests: XCTestCase { try await group.addMembersByInboxId(inboxIds: [fixtures.fredClient.inboxID]) try await group.sync() - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, @@ -260,7 +274,7 @@ class GroupTests: XCTestCase { let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address, fixtures.fred.address]) try await group.sync() - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, @@ -272,7 +286,7 @@ class GroupTests: XCTestCase { try await group.sync() - let newMembers = try group.members.map(\.inboxId).sorted() + let newMembers = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, fixtures.aliceClient.inboxID, @@ -287,7 +301,7 @@ class GroupTests: XCTestCase { let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address, fixtures.fred.address]) try await group.sync() - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, @@ -299,7 +313,7 @@ class GroupTests: XCTestCase { try await group.sync() - let newMembers = try group.members.map(\.inboxId).sorted() + let newMembers = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, fixtures.aliceClient.inboxID, @@ -323,7 +337,7 @@ class GroupTests: XCTestCase { let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address, fixtures.fred.address]) try await group.sync() - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, @@ -345,7 +359,7 @@ class GroupTests: XCTestCase { try await group.sync() - let newMembers = try group.members.map(\.inboxId).sorted() + let newMembers = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([ fixtures.bobClient.inboxID, fixtures.aliceClient.inboxID, @@ -786,7 +800,7 @@ class GroupTests: XCTestCase { try await fixtures.bobClient.contacts.allowInboxes(inboxIds: [fixtures.aliceClient.inboxID]) - var alixMember = try boGroup.members.first(where: { member in member.inboxId == fixtures.aliceClient.inboxID }) + var alixMember = try await boGroup.members.first(where: { member in member.inboxId == fixtures.aliceClient.inboxID }) XCTAssertEqual(alixMember?.consentState, .allowed) isInboxAllowed = try await fixtures.bobClient.contacts.isInboxAllowed(inboxId: fixtures.aliceClient.inboxID) @@ -796,7 +810,7 @@ class GroupTests: XCTestCase { try await fixtures.bobClient.contacts.denyInboxes(inboxIds: [fixtures.aliceClient.inboxID]) - alixMember = try boGroup.members.first(where: { member in member.inboxId == fixtures.aliceClient.inboxID }) + alixMember = try await boGroup.members.first(where: { member in member.inboxId == fixtures.aliceClient.inboxID }) XCTAssertEqual(alixMember?.consentState, .denied) isInboxAllowed = try await fixtures.bobClient.contacts.isInboxAllowed(inboxId: fixtures.aliceClient.inboxID) @@ -941,7 +955,7 @@ class GroupTests: XCTestCase { await withThrowingTaskGroup(of: [Member].self) { taskGroup in for group in groups { taskGroup.addTask { - return try group.members + return try await group.members } } } diff --git a/Tests/XMTPTests/V3ClientTests.swift b/Tests/XMTPTests/V3ClientTests.swift index a4ed952d..4cb7d59e 100644 --- a/Tests/XMTPTests/V3ClientTests.swift +++ b/Tests/XMTPTests/V3ClientTests.swift @@ -64,7 +64,7 @@ class V3ClientTests: XCTestCase { func testsCanCreateGroup() async throws { let fixtures = try await localFixtures() let group = try await fixtures.boV3Client.conversations.newGroup(with: [fixtures.caroV2V3.address]) - let members = try group.members.map(\.inboxId).sorted() + let members = try await group.members.map(\.inboxId).sorted() XCTAssertEqual([fixtures.caroV2V3Client.inboxID, fixtures.boV3Client.inboxID].sorted(), members) await assertThrowsAsyncError( @@ -123,7 +123,7 @@ class V3ClientTests: XCTestCase { try await fixtures.boV3Client.contacts.allowInboxes(inboxIds: [fixtures.caroV2V3Client.inboxID]) - var caroMember = try boGroup.members.first(where: { member in member.inboxId == fixtures.caroV2V3Client.inboxID }) + var caroMember = try await boGroup.members.first(where: { member in member.inboxId == fixtures.caroV2V3Client.inboxID }) XCTAssertEqual(caroMember?.consentState, .allowed) isInboxAllowed = try await fixtures.boV3Client.contacts.isInboxAllowed(inboxId: fixtures.caroV2V3Client.inboxID) @@ -136,7 +136,7 @@ class V3ClientTests: XCTestCase { XCTAssert(!isAddressDenied) try await fixtures.boV3Client.contacts.denyInboxes(inboxIds: [fixtures.caroV2V3Client.inboxID]) - caroMember = try boGroup.members.first(where: { member in member.inboxId == fixtures.caroV2V3Client.inboxID }) + caroMember = try await boGroup.members.first(where: { member in member.inboxId == fixtures.caroV2V3Client.inboxID }) XCTAssertEqual(caroMember?.consentState, .denied) isInboxAllowed = try await fixtures.boV3Client.contacts.isInboxAllowed(inboxId: fixtures.caroV2V3Client.inboxID) diff --git a/XMTP.podspec b/XMTP.podspec index 2071549c..8601d828 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "XMTP" - spec.version = "0.14.14" + spec.version = "0.14.16" 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.5.8-beta6' + spec.dependency 'LibXMTP', '= 0.5.8-beta7' end diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b86f6275..60210e9d 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.git", "state" : { - "revision" : "9398f5516b18044bb94e5d21dabd7a5ddfc25062", - "version" : "0.5.8-beta6" + "revision" : "859333faaddc04128443709182bbcbf41b2209a5", + "version" : "0.5.8-beta7" } }, {