diff --git a/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings b/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings index fd0c94b2e69e400cdce3077fb499b0139ac3e3a8..770e77eb5a33a73b4d48b44b702888e28f51682a 100644 Binary files a/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings and b/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings differ diff --git a/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h b/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h index 15e54413e58bd26f6498c353c161b67e8d7c92e5..40a89ee2ee4afde62d99dcdcac9ef77d09c7c663 100644 --- a/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h +++ b/Frameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h @@ -1182,7 +1182,26 @@ the first contact is the leader/creator of the group. All subsequent members are ordered by their ID. Returns: - - []byte - a JSON marshalled version of the member list. + - []byte - JSON marshalled [group.Membership], which is an array of + [group.Member]. + +Example JSON [group.Membership] return: + [ + { + "ID": "U4x/lrFkvxuXu59LtHLon1sUhPJSCcnZND6SugndnVID", + "DhKey": { + "Value": 3534334367214237261, + "Fingerprint": 16801541511233098363 + } + }, + { + "ID": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", + "DhKey": { + "Value": 7497468244883513247, + "Fingerprint": 16801541511233098363 + } + } + ] */ - (NSData* _Nullable)getMembership:(NSError* _Nullable* _Nullable)error; /** diff --git a/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings b/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings index d8ecef8868deb6bd74c6e6ff23c6a72fb1d377e5..4f732858669f4204798186a99b8dd9687ff7b788 100644 Binary files a/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings and b/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings differ diff --git a/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h b/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h index 15e54413e58bd26f6498c353c161b67e8d7c92e5..40a89ee2ee4afde62d99dcdcac9ef77d09c7c663 100644 --- a/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h +++ b/Frameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h @@ -1182,7 +1182,26 @@ the first contact is the leader/creator of the group. All subsequent members are ordered by their ID. Returns: - - []byte - a JSON marshalled version of the member list. + - []byte - JSON marshalled [group.Membership], which is an array of + [group.Member]. + +Example JSON [group.Membership] return: + [ + { + "ID": "U4x/lrFkvxuXu59LtHLon1sUhPJSCcnZND6SugndnVID", + "DhKey": { + "Value": 3534334367214237261, + "Fingerprint": 16801541511233098363 + } + }, + { + "ID": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", + "DhKey": { + "Value": 7497468244883513247, + "Fingerprint": 16801541511233098363 + } + } + ] */ - (NSData* _Nullable)getMembership:(NSError* _Nullable* _Nullable)error; /** diff --git a/Sources/XXClient/Group/Functors/GroupGetMembership.swift b/Sources/XXClient/Group/Functors/GroupGetMembership.swift index 1d78a2486781187817b022a96741b12c8217cbf6..4df1694412ca0e82001c18ec53938f7855f6778b 100644 --- a/Sources/XXClient/Group/Functors/GroupGetMembership.swift +++ b/Sources/XXClient/Group/Functors/GroupGetMembership.swift @@ -2,9 +2,9 @@ import Bindings import XCTestDynamicOverlay public struct GroupGetMembership { - public var run: () throws -> Data + public var run: () throws -> [GroupMember] - public func callAsFunction() throws -> Data { + public func callAsFunction() throws -> [GroupMember] { try run() } } @@ -12,7 +12,8 @@ public struct GroupGetMembership { extension GroupGetMembership { public static func live(_ bindingsGroup: BindingsGroup) -> GroupGetMembership { GroupGetMembership { - try bindingsGroup.getMembership() + let data = try bindingsGroup.getMembership() + return try [GroupMember].decode(data) } } } diff --git a/Sources/XXClient/Helpers/ConvertJsonNumberToString.swift b/Sources/XXClient/Helpers/ConvertJsonNumberToString.swift new file mode 100644 index 0000000000000000000000000000000000000000..293f1ca640c4c69fc3e3958329b51671ff6b3508 --- /dev/null +++ b/Sources/XXClient/Helpers/ConvertJsonNumberToString.swift @@ -0,0 +1,19 @@ +import Foundation + +func convertJsonNumberToString( + in input: Data, + at key: String +) -> Data { + guard var string = String(data: input, encoding: .utf8) else { + return input + } + string = string.replacingOccurrences( + of: #""\#(key)"( *):( *)([0-9]+)( *)(,*)"#, + with: #""\#(key)"$1:$2"$3"$4$5"#, + options: [.regularExpression] + ) + guard let output = string.data(using: .utf8) else { + return input + } + return output +} diff --git a/Sources/XXClient/Models/DHKey.swift b/Sources/XXClient/Models/DHKey.swift new file mode 100644 index 0000000000000000000000000000000000000000..69380ede4069535867cfc0a65a3ca77c6fca5b1f --- /dev/null +++ b/Sources/XXClient/Models/DHKey.swift @@ -0,0 +1,23 @@ +import Foundation + +public struct DHKey: Equatable { + public init(value: String, fingerprint: UInt64) { + self.value = value + self.fingerprint = fingerprint + } + + public var value: String + public var fingerprint: UInt64 +} + +extension DHKey: Decodable { + enum CodingKeys: String, CodingKey { + case value = "Value" + case fingerprint = "Fingerprint" + } + + public static func decode(_ data: Data) throws -> Self { + let data = convertJsonNumberToString(in: data, at: "Value") + return try JSONDecoder().decode(Self.self, from: data) + } +} diff --git a/Sources/XXClient/Models/GroupMember.swift b/Sources/XXClient/Models/GroupMember.swift new file mode 100644 index 0000000000000000000000000000000000000000..b08d71c250c9af5d4c6ca504dda9fc8586117eb5 --- /dev/null +++ b/Sources/XXClient/Models/GroupMember.swift @@ -0,0 +1,30 @@ +import Foundation + +public struct GroupMember: Equatable { + public init(id: Data, dhKey: DHKey) { + self.id = id + self.dhKey = dhKey + } + + public var id: Data + public var dhKey: DHKey +} + +extension GroupMember: Decodable { + enum CodingKeys: String, CodingKey { + case id = "ID" + case dhKey = "DhKey" + } + + public static func decode(_ data: Data) throws -> Self { + let data = convertJsonNumberToString(in: data, at: "Value") + return try JSONDecoder().decode(Self.self, from: data) + } +} + +extension Array where Element == GroupMember { + public static func decode(_ data: Data) throws -> Self { + let data = convertJsonNumberToString(in: data, at: "Value") + return try JSONDecoder().decode(Self.self, from: data) + } +} diff --git a/Tests/XXClientTests/Helpers/ConvertJsonNumberToStringTests.swift b/Tests/XXClientTests/Helpers/ConvertJsonNumberToStringTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..db3a4b2edc77a81ae493a03b9294389ff7c6a203 --- /dev/null +++ b/Tests/XXClientTests/Helpers/ConvertJsonNumberToStringTests.swift @@ -0,0 +1,84 @@ +import CustomDump +import XCTest +@testable import XXClient + +final class ConvertJsonNumberToStringTests: XCTestCase { + func testConverting() { + assert( + input: #"{"number":1234567890,"text":"hello"}"#, + key: "number", + expected: #"{"number":"1234567890","text":"hello"}"# + ) + + assert( + input: #"{"text":"hello","number":1234567890}"#, + key: "number", + expected: #"{"text":"hello","number":"1234567890"}"# + ) + + assert( + input: #"{ "number" : 1234567890 , "text" : "hello" }"#, + key: "number", + expected: #"{ "number" : "1234567890" , "text" : "hello" }"# + ) + + assert( + input: #"{ "text" : "hello" , "number" : 1234567890 }"#, + key: "number", + expected: #"{ "text" : "hello" , "number" : "1234567890" }"# + ) + + assert( + input: """ + { + "number": 1234567890, + "text": "hello" + } + """, + key: "number", + expected: """ + { + "number": "1234567890", + "text": "hello" + } + """ + ) + + assert( + input: """ + { + "text": "hello", + "number": 1234567890 + } + """, + key: "number", + expected: """ + { + "text": "hello", + "number": "1234567890" + } + """ + ) + } +} + +private func assert( + input: String, + key: String, + expected: String, + file: StaticString = #file, + line: UInt = #line +) { + XCTAssertNoDifference( + String( + data: convertJsonNumberToString( + in: input.data(using: .utf8)!, + at: key + ), + encoding: .utf8 + )!, + expected, + file: file, + line: line + ) +} diff --git a/Tests/XXClientTests/Models/DHKeyTests.swift b/Tests/XXClientTests/Models/DHKeyTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..380d69edb5a1536c6277dbc82dd8b01f65388ca3 --- /dev/null +++ b/Tests/XXClientTests/Models/DHKeyTests.swift @@ -0,0 +1,23 @@ +import CustomDump +import XCTest +@testable import XXClient + +final class DHKeyTests: XCTestCase { + func testCoding() throws { + let value = "1759426033802606996617132861414734059978289057332806031357800676138355264622676606691435603603751978195460163638145821347601916259127578968570412642641025630452893097179266022832268525346700655861033031712000288680395716922501450233258587788020541937373196899001184700899008948530359980753630443486308876999029238453979779103124291315202352475056237021681172884599194016245219278368648568458514708567045834427853469072638665888791358582182353417065794210125797368093469194927663862565508608719835557592421245749381164023134450699040591219966988201315627676532245052123725278573237006510683695959381015415110970848376498637637944431576313526294020390694483472829278364602405292767170719547347485307956614210210673321959886410245334772057212077704024337636501108566655549055129066343309591274727538343075929837698653965640646190405582788894021694347212874155979958144038307500444865955516612526623220973497735316081265793063949" + let fingerprint: UInt64 = 15989433043166758754 + let jsonString = """ + { + "Value": \(value), + "Fingerprint": \(fingerprint) + } + """ + let jsonData = jsonString.data(using: .utf8)! + let model = try DHKey.decode(jsonData) + + XCTAssertNoDifference(model, DHKey( + value: value, + fingerprint: fingerprint + )) + } +} diff --git a/Tests/XXClientTests/Models/FactTests.swift b/Tests/XXClientTests/Models/FactTests.swift index fbe588c534f2edf4a758e1fc32ee96ff789c4dd7..2ba1da9ac9da82e174cee504e8708bb6794e9a81 100644 --- a/Tests/XXClientTests/Models/FactTests.swift +++ b/Tests/XXClientTests/Models/FactTests.swift @@ -25,4 +25,17 @@ final class FactTests: XCTestCase { XCTAssertNoDifference(decodedModel, model) } + + func testCodingArray() throws { + let models = [ + Fact(fact: "abcd", type: 0), + Fact(fact: "efgh", type: 1), + Fact(fact: "ijkl", type: 2), + ] + + let encodedModels = try models.encode() + let decodedModels = try [Fact].decode(encodedModels) + + XCTAssertNoDifference(models, decodedModels) + } } diff --git a/Tests/XXClientTests/Models/GroupMemberTests.swift b/Tests/XXClientTests/Models/GroupMemberTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..a682dfe97ca7c707845c678dca6cba006e4adf5a --- /dev/null +++ b/Tests/XXClientTests/Models/GroupMemberTests.swift @@ -0,0 +1,36 @@ +import CustomDump +import XCTest +@testable import XXClient + +final class GroupMemberTests: XCTestCase { + func testCoding() throws { + let idB64 = "U4x/lrFkvxuXu59LtHLon1sUhPJSCcnZND6SugndnVID" + let dhKeyValue = "1759426033802606996617132861414734059978289057332806031357800676138355264622676606691435603603751978195460163638145821347601916259127578968570412642641025630452893097179266022832268525346700655861033031712000288680395716922501450233258587788020541937373196899001184700899008948530359980753630443486308876999029238453979779103124291315202352475056237021681172884599194016245219278368648568458514708567045834427853469072638665888791358582182353417065794210125797368093469194927663862565508608719835557592421245749381164023134450699040591219966988201315627676532245052123725278573237006510683695959381015415110970848376498637637944431576313526294020390694483472829278364602405292767170719547347485307956614210210673321959886410245334772057212077704024337636501108566655549055129066343309591274727538343075929837698653965640646190405582788894021694347212874155979958144038307500444865955516612526623220973497735316081265793063949" + let dhKeyFingerprint: UInt64 = 15989433043166758754 + let jsonString = """ + { + "ID": "\(idB64)", + "DhKey": { + "Value": \(dhKeyValue), + "Fingerprint": \(dhKeyFingerprint) + } + } + """ + let jsonData = jsonString.data(using: .utf8)! + let model = try GroupMember.decode(jsonData) + + XCTAssertNoDifference(model, GroupMember( + id: Data(base64Encoded: idB64)!, + dhKey: DHKey( + value: dhKeyValue, + fingerprint: dhKeyFingerprint + ) + )) + + let jsonArrayString = "[\(jsonString)]" + let jsonArrayData = jsonArrayString.data(using: .utf8)! + let decodedModels = try [GroupMember].decode(jsonArrayData) + + XCTAssertNoDifference(decodedModels, [model]) + } +}