diff --git a/Docs/XXMessengerClient.md b/Docs/XXMessengerClient.md index 4db0098b6a4f9a18df063189c172ec8a3f275f56..c8fc3e7dd8b6ba45738f1e0914fa758f6b5a0d28 100644 --- a/Docs/XXMessengerClient.md +++ b/Docs/XXMessengerClient.md @@ -27,6 +27,9 @@ let messenger: Messenger = .live(environment) Example: ```swift +// allow cancellation of auth callbacks registration: +var authCallbacksCancellable: Cancellable? + func start(messenger: Messenger) throws { // check if messenger is loaded: if messenger.isLoaded() == false { @@ -42,6 +45,13 @@ func start(messenger: Messenger) throws { // start messenger's network follower: try messenger.start() + // register auth callbacks before connecting: + authCallbacksCancellable = messenger.registerAuthCallbacks( + AuthCallbacks(handle: { callback in + // implement auth callbacks handling + }) + ) + // check if messenger is connected: if messenger.isConnected() == false { // start end-to-end connection: diff --git a/Sources/XXMessengerClient/Messenger/Functors/MessengerConnect.swift b/Sources/XXMessengerClient/Messenger/Functors/MessengerConnect.swift index baf5ae9ca017bd9cb89acdfaacdf0fde1fd6846c..d871f5341027e36456e622959b30e36ee26da163 100644 --- a/Sources/XXMessengerClient/Messenger/Functors/MessengerConnect.swift +++ b/Sources/XXMessengerClient/Messenger/Functors/MessengerConnect.swift @@ -21,6 +21,7 @@ extension MessengerConnect { } env.e2e.set(try env.login( cMixId: cMix.getId(), + authCallbacks: env.authCallbacks.registered(), identity: try cMix.makeLegacyReceptionIdentity(), e2eParamsJSON: env.getE2EParams() )) diff --git a/Sources/XXMessengerClient/Messenger/Functors/MessengerRegisterAuthCallbacks.swift b/Sources/XXMessengerClient/Messenger/Functors/MessengerRegisterAuthCallbacks.swift new file mode 100644 index 0000000000000000000000000000000000000000..ad184957aae0c17d24a457d77d047506541eb922 --- /dev/null +++ b/Sources/XXMessengerClient/Messenger/Functors/MessengerRegisterAuthCallbacks.swift @@ -0,0 +1,24 @@ +import XXClient +import XCTestDynamicOverlay + +public struct MessengerRegisterAuthCallbacks { + public var run: (AuthCallbacks) -> Cancellable + + public func callAsFunction(_ authCallbacks: AuthCallbacks) -> Cancellable { + run(authCallbacks) + } +} + +extension MessengerRegisterAuthCallbacks { + public static func live(_ env: MessengerEnvironment) -> MessengerRegisterAuthCallbacks { + MessengerRegisterAuthCallbacks { callback in + env.authCallbacks.register(callback) + } + } +} + +extension MessengerRegisterAuthCallbacks { + public static let unimplemented = MessengerRegisterAuthCallbacks( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift index 30d63efdfac2716a0d3efc7f0a614eafeaf2e852..c9563fe8a4db09ab0e7c9e8e1c6dfcfcdb7ab3be 100644 --- a/Sources/XXMessengerClient/Messenger/Messenger.swift +++ b/Sources/XXMessengerClient/Messenger/Messenger.swift @@ -8,6 +8,7 @@ public struct Messenger { public var create: MessengerCreate public var isLoaded: MessengerIsLoaded public var load: MessengerLoad + public var registerAuthCallbacks: MessengerRegisterAuthCallbacks public var start: MessengerStart public var isConnected: MessengerIsConnected public var connect: MessengerConnect @@ -29,6 +30,7 @@ extension Messenger { create: .live(env), isLoaded: .live(env), load: .live(env), + registerAuthCallbacks: .live(env), start: .live(env), isConnected: .live(env), connect: .live(env), @@ -51,6 +53,7 @@ extension Messenger { create: .unimplemented, isLoaded: .unimplemented, load: .unimplemented, + registerAuthCallbacks: .unimplemented, start: .unimplemented, isConnected: .unimplemented, connect: .unimplemented, diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift index 6ac78b1e8dc416db224b2e6dd3be699c68b31fb6..ad94b5ce8b97749231abb6b47ef4547c9c9bba03 100644 --- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift +++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift @@ -3,6 +3,7 @@ import XXClient import XCTestDynamicOverlay public struct MessengerEnvironment { + public var authCallbacks: AuthCallbacksRegistry public var cMix: Stored<CMix?> public var downloadNDF: DownloadAndVerifySignedNdf public var e2e: Stored<E2E?> @@ -34,6 +35,7 @@ extension MessengerEnvironment { public static func live() -> MessengerEnvironment { MessengerEnvironment( + authCallbacks: .live(), cMix: .inMemory(), downloadNDF: .live, e2e: .inMemory(), @@ -60,6 +62,7 @@ extension MessengerEnvironment { extension MessengerEnvironment { public static let unimplemented = MessengerEnvironment( + authCallbacks: .unimplemented, cMix: .unimplemented(), downloadNDF: .unimplemented, e2e: .unimplemented(), diff --git a/Sources/XXMessengerClient/Utils/AuthCallbacksRegistry.swift b/Sources/XXMessengerClient/Utils/AuthCallbacksRegistry.swift new file mode 100644 index 0000000000000000000000000000000000000000..d0c50b8149c77ecd54e208858d895285c6d7408c --- /dev/null +++ b/Sources/XXMessengerClient/Utils/AuthCallbacksRegistry.swift @@ -0,0 +1,36 @@ +import XXClient +import XCTestDynamicOverlay +import Foundation + +public struct AuthCallbacksRegistry { + public var register: (AuthCallbacks) -> Cancellable + public var registered: () -> AuthCallbacks +} + +extension AuthCallbacksRegistry { + public static func live() -> AuthCallbacksRegistry { + class Registry { + var authCallbacks: [UUID: AuthCallbacks] = [:] + } + let registry = Registry() + return AuthCallbacksRegistry( + register: { authCallbacks in + let id = UUID() + registry.authCallbacks[id] = authCallbacks + return Cancellable { registry.authCallbacks[id] = nil } + }, + registered: { + AuthCallbacks { callback in + registry.authCallbacks.values.forEach { $0.handle(callback) } + } + } + ) + } +} + +extension AuthCallbacksRegistry { + public static let unimplemented = AuthCallbacksRegistry( + register: XCTUnimplemented("\(Self.self).register", placeholder: Cancellable {}), + registered: XCTUnimplemented("\(Self.self).registered", placeholder: AuthCallbacks { _ in }) + ) +} diff --git a/Tests/XXMessengerClientTests/Messenger/Functors/MessengerConnectTests.swift b/Tests/XXMessengerClientTests/Messenger/Functors/MessengerConnectTests.swift index fb780353631b32f3f4bcf686bf60f3c3e38703c3..7654bc26adca7ccf82be5d8836b954af40e38a73 100644 --- a/Tests/XXMessengerClientTests/Messenger/Functors/MessengerConnectTests.swift +++ b/Tests/XXMessengerClientTests/Messenger/Functors/MessengerConnectTests.swift @@ -8,13 +8,14 @@ final class MessengerConnectTests: XCTestCase { struct DidLogIn: Equatable { var ephemeral: Bool var cMixId: Int - var authCallbacksProvided: Bool var identity: ReceptionIdentity var e2eParamsJSON: Data } var didLogIn: [DidLogIn] = [] + var didLogInWithAuthCallbacks: [AuthCallbacks?] = [] var didSetE2E: [E2E?] = [] + var didHandleAuthCallbacks: [AuthCallbacks.Callback] = [] let cMixId = 1234 let receptionId = ReceptionIdentity.stub @@ -29,14 +30,19 @@ final class MessengerConnectTests: XCTestCase { } env.e2e.set = { didSetE2E.append($0) } env.getE2EParams.run = { e2eParams } + env.authCallbacks.registered = { + AuthCallbacks { callback in + didHandleAuthCallbacks.append(callback) + } + } env.login.run = { ephemeral, cMixId, authCallbacks, identity, e2eParamsJSON in didLogIn.append(.init( ephemeral: ephemeral, cMixId: cMixId, - authCallbacksProvided: authCallbacks != nil, identity: identity, e2eParamsJSON: e2eParamsJSON )) + didLogInWithAuthCallbacks.append(authCallbacks) return .unimplemented } let connect: MessengerConnect = .live(env) @@ -47,12 +53,19 @@ final class MessengerConnectTests: XCTestCase { DidLogIn( ephemeral: false, cMixId: 1234, - authCallbacksProvided: false, identity: .stub, e2eParamsJSON: e2eParams ) ]) + XCTAssertEqual(didLogInWithAuthCallbacks.compactMap { $0 }.count, 1) XCTAssertEqual(didSetE2E.compactMap { $0 }.count, 1) + + didLogInWithAuthCallbacks.forEach { authCallbacks in + [AuthCallbacks.Callback].stubs.forEach { callback in + authCallbacks?.handle(callback) + } + } + XCTAssertNoDifference(didHandleAuthCallbacks, .stubs) } func testConnectWithoutCMix() { @@ -79,6 +92,7 @@ final class MessengerConnectTests: XCTestCase { cMix.makeLegacyReceptionIdentity.run = { throw error } return cMix } + env.authCallbacks.registered = { .unimplemented } let connect: MessengerConnect = .live(env) XCTAssertThrowsError(try connect()) { err in @@ -97,6 +111,7 @@ final class MessengerConnectTests: XCTestCase { cMix.makeLegacyReceptionIdentity.run = { .stub } return cMix } + env.authCallbacks.registered = { .unimplemented } env.getE2EParams.run = { "e2e-params".data(using: .utf8)! } env.login.run = { _, _, _, _, _ in throw error } let connect: MessengerConnect = .live(env) diff --git a/Tests/XXMessengerClientTests/Messenger/Functors/MessengerRegisterAuthCallbacksTests.swift b/Tests/XXMessengerClientTests/Messenger/Functors/MessengerRegisterAuthCallbacksTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..d7903e2f1b73e03dbe8268561c1ef586bf8a4583 --- /dev/null +++ b/Tests/XXMessengerClientTests/Messenger/Functors/MessengerRegisterAuthCallbacksTests.swift @@ -0,0 +1,36 @@ +import XCTest +import XXClient +@testable import XXMessengerClient +import CustomDump + +final class MessengerRegisterAuthCallbacksTests: XCTestCase { + func testRegisterAuthCallbacks() { + var registeredAuthCallbacks: [AuthCallbacks] = [] + var didHandleCallbacks: [AuthCallbacks.Callback] = [] + var didCancelRegisteredAuthCallbacks = 0 + + var env: MessengerEnvironment = .unimplemented + env.authCallbacks.register = { authCallbacks in + registeredAuthCallbacks.append(authCallbacks) + return Cancellable { didCancelRegisteredAuthCallbacks += 1 } + } + let registerAuthCallbacks: MessengerRegisterAuthCallbacks = .live(env) + let cancellable = registerAuthCallbacks(AuthCallbacks { callback in + didHandleCallbacks.append(callback) + }) + + XCTAssertEqual(registeredAuthCallbacks.count, 1) + + registeredAuthCallbacks.forEach { authCallbacks in + [AuthCallbacks.Callback].stubs.forEach { callback in + authCallbacks.handle(callback) + } + } + + XCTAssertNoDifference(didHandleCallbacks, .stubs) + + cancellable.cancel() + + XCTAssertEqual(didCancelRegisteredAuthCallbacks, 1) + } +} diff --git a/Tests/XXMessengerClientTests/TestHelpers/AuthCallback.Callback+stubs.swift b/Tests/XXMessengerClientTests/TestHelpers/AuthCallback.Callback+stubs.swift new file mode 100644 index 0000000000000000000000000000000000000000..9617c0b21319cb7d5624e0bcd3a6b22fbff3bc69 --- /dev/null +++ b/Tests/XXMessengerClientTests/TestHelpers/AuthCallback.Callback+stubs.swift @@ -0,0 +1,24 @@ +import XXClient + +extension Array where Element == AuthCallbacks.Callback { + static let stubs: [AuthCallbacks.Callback] = [ + .confirm( + contact: "contact-1".data(using: .utf8)!, + receptionId: "reception-id-1".data(using: .utf8)!, + ephemeralId: 1, + roundId: 1 + ), + .request( + contact: "contact-2".data(using: .utf8)!, + receptionId: "reception-id-2".data(using: .utf8)!, + ephemeralId: 2, + roundId: 2 + ), + .reset( + contact: "contact-3".data(using: .utf8)!, + receptionId: "reception-id-3".data(using: .utf8)!, + ephemeralId: 3, + roundId: 3 + ), + ] +} diff --git a/Tests/XXMessengerClientTests/Utils/AuthCallbacksRegistryTests.swift b/Tests/XXMessengerClientTests/Utils/AuthCallbacksRegistryTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..1d1ba7b2f6226e9283fc60d8656dbe0b317113a1 --- /dev/null +++ b/Tests/XXMessengerClientTests/Utils/AuthCallbacksRegistryTests.swift @@ -0,0 +1,43 @@ +import XCTest +import XXClient +@testable import XXMessengerClient +import CustomDump + +final class AuthCallbacksRegistryTests: XCTestCase { + func testRegistry() { + var firstAuthCallbacksDidHandle: [AuthCallbacks.Callback] = [] + var secondAuthCallbacksDidHandle: [AuthCallbacks.Callback] = [] + + let firstAuthCallbacks = AuthCallbacks { callback in + firstAuthCallbacksDidHandle.append(callback) + } + let secondAuthCallbacks = AuthCallbacks { callback in + secondAuthCallbacksDidHandle.append(callback) + } + let messengerAuthCallbacks: AuthCallbacksRegistry = .live() + let registeredAuthCallbacks = messengerAuthCallbacks.registered() + let firstAuthCallbacksCancellable = messengerAuthCallbacks.register(firstAuthCallbacks) + let secondAuthCallbacksCancellable = messengerAuthCallbacks.register(secondAuthCallbacks) + + let firstCallback = [AuthCallbacks.Callback].stubs[0] + registeredAuthCallbacks.handle(firstCallback) + + XCTAssertNoDifference(firstAuthCallbacksDidHandle, [firstCallback]) + XCTAssertNoDifference(secondAuthCallbacksDidHandle, [firstCallback]) + + firstAuthCallbacksCancellable.cancel() + let secondCallback = [AuthCallbacks.Callback].stubs[1] + registeredAuthCallbacks.handle(secondCallback) + + XCTAssertNoDifference(firstAuthCallbacksDidHandle, [firstCallback]) + XCTAssertNoDifference(secondAuthCallbacksDidHandle, [firstCallback, secondCallback]) + + secondAuthCallbacksCancellable.cancel() + + let thirdCallback = [AuthCallbacks.Callback].stubs[2] + registeredAuthCallbacks.handle(thirdCallback) + + XCTAssertNoDifference(firstAuthCallbacksDidHandle, [firstCallback]) + XCTAssertNoDifference(secondAuthCallbacksDidHandle, [firstCallback, secondCallback]) + } +} diff --git a/Tests/XXMessengerClientTests/Messenger/Utils/StoredTests.swift b/Tests/XXMessengerClientTests/Utils/StoredTests.swift similarity index 100% rename from Tests/XXMessengerClientTests/Messenger/Utils/StoredTests.swift rename to Tests/XXMessengerClientTests/Utils/StoredTests.swift