diff --git a/GliaWidgets/Public/Glia/Glia+EngagementSetup.swift b/GliaWidgets/Public/Glia/Glia+EngagementSetup.swift index fae09f96b..94a403dfd 100644 --- a/GliaWidgets/Public/Glia/Glia+EngagementSetup.swift +++ b/GliaWidgets/Public/Glia/Glia+EngagementSetup.swift @@ -142,7 +142,7 @@ extension Glia { ) { let engagementLaunching: EngagementCoordinator.EngagementLaunching - switch (pendingInteraction.hasPendingInteraction, engagementKind) { + switch (pendingInteraction?.hasPendingInteraction ?? false, engagementKind) { case (false, _): // if there is no pending Secure Conversation, open regular flow. engagementLaunching = .direct(kind: engagementKind) diff --git a/GliaWidgets/Public/Glia/Glia+EntryWidget.swift b/GliaWidgets/Public/Glia/Glia+EntryWidget.swift index 99ab32470..0d59c9f70 100644 --- a/GliaWidgets/Public/Glia/Glia+EntryWidget.swift +++ b/GliaWidgets/Public/Glia/Glia+EntryWidget.swift @@ -26,7 +26,7 @@ extension Glia { isAuthenticated: environment.isAuthenticated, hasPendingInteraction: { [weak self] in guard let self else { return false } - return pendingInteraction.hasPendingInteraction + return pendingInteraction?.hasPendingInteraction ?? false } ) ) diff --git a/GliaWidgets/Public/Glia/Glia.swift b/GliaWidgets/Public/Glia/Glia.swift index 9229d4267..c5326e9dc 100644 --- a/GliaWidgets/Public/Glia/Glia.swift +++ b/GliaWidgets/Public/Glia/Glia.swift @@ -135,7 +135,7 @@ public class Glia { // // Currently it's used to know if we have to force a visitor to SecureMessaging screen, // once they try to start an engagement with media type other than `messaging`. - let pendingInteraction: SecureConversations.PendingInteraction + var pendingInteraction: SecureConversations.PendingInteraction? init(environment: Environment) { self.environment = environment @@ -179,7 +179,6 @@ public class Glia { viewFactory: viewFactory ) ) - pendingInteraction = .init(environment: .init(with: environment.coreSdk)) } /// Setup SDK using specific engagement configuration without starting the engagement. @@ -258,7 +257,21 @@ public class Glia { // Configuration completion handler has to be called in any case, // at the end of the scope, whether there's ongoing engagement or not. - defer { completion(.success(())) } + defer { + // PendingInteraction is essential part of SC flow, so it's not + // valid to consider SDK configured if PI is not created. + do { + pendingInteraction = try .init(environment: .init(with: environment.coreSdk)) + completion(.success(())) + } catch let error as SecureConversations.PendingInteraction.Error { + switch error { + case .subscriptionFailure: + completion(.failure(GliaError.internalEventSubscriptionFailure)) + } + } catch { + completion(.failure(GliaError.internalError)) + } + } guard let currentEngagement = self.environment.coreSdk.getCurrentEngagement() else { return } diff --git a/GliaWidgets/Public/GliaError.swift b/GliaWidgets/Public/GliaError.swift index 51af1d6aa..443c06e14 100644 --- a/GliaWidgets/Public/GliaError.swift +++ b/GliaWidgets/Public/GliaError.swift @@ -30,4 +30,7 @@ public enum GliaError: Error { /// Internal error. case internalError + + /// Internal event subscription failure. + case internalEventSubscriptionFailure } diff --git a/GliaWidgets/SecureConversations/SecureConversations.PendingInteraction.swift b/GliaWidgets/SecureConversations/SecureConversations.PendingInteraction.swift index de9051133..25acc2ae3 100644 --- a/GliaWidgets/SecureConversations/SecureConversations.PendingInteraction.swift +++ b/GliaWidgets/SecureConversations/SecureConversations.PendingInteraction.swift @@ -9,21 +9,34 @@ extension SecureConversations { private(set) var pendingStatusCancellationToken: String? private(set) var unreadMessageCountCancellationToken: String? - init(environment: Environment) { + init(environment: Environment) throws { self.environment = environment - self.pendingStatusCancellationToken = environment.observePendingSecureConversationsStatus { [weak self] result in + let pendingStatusCancellationToken = environment.observePendingSecureConversationsStatus { [weak self] result in guard let self else { return } // At this point it is enough to know if there is a pending conversation, // so no need to handle error. pendingStatus = (try? result.get()) ?? false } - self.unreadMessageCountCancellationToken = environment.observeSecureConversationsUnreadMessageCount { [weak self] result in + + guard pendingStatusCancellationToken != nil else { + throw Error.subscriptionFailure(.pendingStatus) + } + + self.pendingStatusCancellationToken = pendingStatusCancellationToken + + let unreadMessageCountCancellationToken = environment.observeSecureConversationsUnreadMessageCount { [weak self] result in guard let self else { return } // At this point it is enough to know if there is an unread message count, // so no need to handle error. unreadMessageCount = (try? result.get()) ?? 0 } + guard unreadMessageCountCancellationToken != nil else { + throw Error.subscriptionFailure(.unreadMessageCount) + } + + self.unreadMessageCountCancellationToken = unreadMessageCountCancellationToken + $pendingStatus.combineLatest($unreadMessageCount) .map { hasPending, unreadCount in hasPending || unreadCount > 0 @@ -52,6 +65,16 @@ extension SecureConversations.PendingInteraction { } } +extension SecureConversations.PendingInteraction { + enum Error: Swift.Error { + enum Subscription { + case unreadMessageCount + case pendingStatus + } + case subscriptionFailure(Subscription) + } +} + extension SecureConversations.PendingInteraction.Environment { init(with client: CoreSdkClient) { self.observePendingSecureConversationsStatus = client.observePendingSecureConversationStatus @@ -63,17 +86,20 @@ extension SecureConversations.PendingInteraction.Environment { #if DEBUG extension SecureConversations.PendingInteraction.Environment { - static let mock = Self( - observePendingSecureConversationsStatus: { _ in nil }, - observeSecureConversationsUnreadMessageCount: { _ in nil }, - unsubscribeFromUnreadCount: { _ in }, - unsubscribeFromPendingStatus: { _ in } - ) + static let mock: Self = { + let uuidGen = UUID.incrementing + return Self( + observePendingSecureConversationsStatus: { _ in uuidGen().uuidString }, + observeSecureConversationsUnreadMessageCount: { _ in uuidGen().uuidString }, + unsubscribeFromUnreadCount: { _ in }, + unsubscribeFromPendingStatus: { _ in } + ) + }() } extension SecureConversations.PendingInteraction { - static func mock(environment: Environment = .mock) -> Self { - .init(environment: environment) + static func mock(environment: Environment = .mock) throws -> Self { + try .init(environment: environment) } } #endif diff --git a/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteraction.Failing.swift b/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteraction.Failing.swift index b125e8798..59bbbdfb0 100644 --- a/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteraction.Failing.swift +++ b/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteraction.Failing.swift @@ -20,7 +20,7 @@ extension SecureConversations.PendingInteraction.Environment { } extension SecureConversations.PendingInteraction { - static func failing() -> Self { - .init(environment: .failing) + static func failing() throws -> Self { + try .init(environment: .failing) } } diff --git a/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteractionTests.swift b/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteractionTests.swift index ceedbab84..895418124 100644 --- a/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteractionTests.swift +++ b/GliaWidgetsTests/SecureConversations/SecureConversations.PendingInteractionTests.swift @@ -18,7 +18,7 @@ final class SecureConversationsPendingInteractionTests: XCTestCase { environment.unsubscribeFromPendingStatus = { _ in } environment.unsubscribeFromUnreadCount = { _ in } - let pendingInteraction = SecureConversations.PendingInteraction(environment: environment) + let pendingInteraction = try SecureConversations.PendingInteraction(environment: environment) // Assert initial pending interaction is false. XCTAssertFalse(pendingInteraction.hasPendingInteraction) // Affect pending secure conversations value by setting it to `true` and assert `hasPendingInteraction`, @@ -36,7 +36,7 @@ final class SecureConversationsPendingInteractionTests: XCTestCase { XCTAssertFalse(pendingInteraction.hasPendingInteraction) } - func test_unsubscribeIsCalledOnDeinit() { + func test_unsubscribeIsCalledOnDeinit() throws { enum Call { case unsubscribeFromPendingStatus case unsubscribeFromUnreadCount @@ -52,8 +52,8 @@ final class SecureConversationsPendingInteractionTests: XCTestCase { environment.unsubscribeFromUnreadCount = { _ in calls.append(.unsubscribeFromUnreadCount) } - var pendingInteraction = SecureConversations.PendingInteraction(environment: environment) - pendingInteraction = .mock() + var pendingInteraction = try SecureConversations.PendingInteraction(environment: environment) + pendingInteraction = try .mock() _ = pendingInteraction XCTAssertEqual(calls, [.unsubscribeFromUnreadCount, .unsubscribeFromPendingStatus]) } diff --git a/GliaWidgetsTests/Sources/Glia/GliaTests.swift b/GliaWidgetsTests/Sources/Glia/GliaTests.swift index 5b3b3d55b..86b868828 100644 --- a/GliaWidgetsTests/Sources/Glia/GliaTests.swift +++ b/GliaWidgetsTests/Sources/Glia/GliaTests.swift @@ -747,10 +747,12 @@ final class GliaTests: XCTestCase { XCTAssertEqual(messages, ["Initialize Glia Widgets SDK", "Setting Unified UI Config"]) } - func test_hasPendingInteractionIfPendingSecureConversationExists() { + func test_hasPendingInteractionIfPendingSecureConversationExists() throws { var gliaEnv = Glia.Environment.failing var logger = CoreSdkClient.Logger.failing let uuidGen = UUID.incrementing + logger.infoClosure = { _, _, _, _ in } + logger.prefixedClosure = { _ in logger } logger.configureLocalLogLevelClosure = { _ in } logger.configureRemoteLogLevelClosure = { _ in } gliaEnv.coreSdk.createLogger = { _ in logger } @@ -765,18 +767,28 @@ final class GliaTests: XCTestCase { } gliaEnv.coreSdk.unsubscribeFromPendingSecureConversationStatus = { _ in } gliaEnv.coreSdk.unsubscribeFromUnreadCount = { _ in } + gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in + completion(.success(())) + } + gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } let sdk = Glia(environment: gliaEnv) + try sdk.configure( + with: .mock(), + theme: .mock() + ) { _ in } - XCTAssertTrue(sdk.pendingInteraction.hasPendingInteraction) + XCTAssertTrue(try XCTUnwrap(sdk.pendingInteraction).hasPendingInteraction) } - func test_hasPendingInteractionIfUnreadMessagesExist() { + func test_hasPendingInteractionIfUnreadMessagesExist() throws { var gliaEnv = Glia.Environment.failing var logger = CoreSdkClient.Logger.failing let uuidGen = UUID.incrementing logger.configureLocalLogLevelClosure = { _ in } logger.configureRemoteLogLevelClosure = { _ in } + logger.infoClosure = { _, _, _, _ in } + logger.prefixedClosure = { _ in logger } gliaEnv.coreSdk.createLogger = { _ in logger } gliaEnv.conditionalCompilation.isDebug = { true } gliaEnv.coreSdk.subscribeForUnreadSCMessageCount = { callback in @@ -789,28 +801,50 @@ final class GliaTests: XCTestCase { } gliaEnv.coreSdk.unsubscribeFromPendingSecureConversationStatus = { _ in } gliaEnv.coreSdk.unsubscribeFromUnreadCount = { _ in } + gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in + completion(.success(())) + } + gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } let sdk = Glia(environment: gliaEnv) - XCTAssertTrue(sdk.pendingInteraction.hasPendingInteraction) + try sdk.configure( + with: .mock(), + theme: .mock() + ) { _ in } + + XCTAssertTrue(try XCTUnwrap(sdk.pendingInteraction).hasPendingInteraction) } - func test_hasPendingInteractionIfNoUnreadMessageAndPendingSecureConversationExist() { + func test_hasPendingInteractionIfNoUnreadMessageAndPendingSecureConversationExist() throws { + var uuidGen = UUID.incrementing var gliaEnv = Glia.Environment.failing var logger = CoreSdkClient.Logger.failing logger.configureLocalLogLevelClosure = { _ in } logger.configureRemoteLogLevelClosure = { _ in } + logger.configureRemoteLogLevelClosure = { _ in } + logger.infoClosure = { _, _, _, _ in } + logger.prefixedClosure = { _ in logger } gliaEnv.coreSdk.createLogger = { _ in logger } gliaEnv.coreSdk.pendingSecureConversationStatus = { $0(.success(false)) } gliaEnv.coreSdk.getSecureUnreadMessageCount = { $0(.success(0)) } gliaEnv.conditionalCompilation.isDebug = { true } - gliaEnv.coreSdk.subscribeForUnreadSCMessageCount = { _ in nil } - gliaEnv.coreSdk.observePendingSecureConversationStatus = { _ in nil } + gliaEnv.coreSdk.subscribeForUnreadSCMessageCount = { _ in uuidGen().uuidString } + gliaEnv.coreSdk.observePendingSecureConversationStatus = { _ in uuidGen().uuidString } gliaEnv.coreSdk.unsubscribeFromPendingSecureConversationStatus = { _ in } gliaEnv.coreSdk.unsubscribeFromUnreadCount = { _ in } + gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in + completion(.success(())) + } + gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } let sdk = Glia(environment: gliaEnv) - XCTAssertFalse(sdk.pendingInteraction.hasPendingInteraction) + try sdk.configure( + with: .mock(), + theme: .mock() + ) { _ in } + + XCTAssertFalse(try XCTUnwrap(sdk.pendingInteraction).hasPendingInteraction) } }