From 1d03c85d8bec9854d391e7c6ecafd87c3585bd4a Mon Sep 17 00:00:00 2001 From: Egor Egorov Date: Wed, 25 Oct 2023 14:57:40 +0300 Subject: [PATCH] Introduce new `configure` interface with non-optional `completion` Commit also includes several improvements: - filtering out empty queueIds; - depracation of old `configure` method; - replace checking interactor existance with configuration one to ensure SDK is configured; - improve error handling in TestingApp MOB-2784 --- .../Public/Glia/Glia+StartEngagement.swift | 9 +- GliaWidgets/Public/Glia/Glia.Deprecated.swift | 23 ++++- .../Glia/Glia.RemoteConfiguration.swift | 2 +- GliaWidgets/Public/Glia/Glia.swift | 47 ++++++---- .../CoreSDKClient.Interface.swift | 3 +- .../CoreSDKConfigurator.Interface.swift | 6 +- .../Call/CallViewController.Mock.swift | 2 +- .../Chat/ChatViewController.Mock.swift | 4 +- .../ChatViewModel/ChatViewModelTests.swift | 6 +- .../Glia/GliaTests+StartEngagement.swift | 67 ++++++++----- GliaWidgetsTests/Sources/Glia/GliaTests.swift | 52 +++++++---- .../Sources/InteractorTests.swift | 8 +- .../ViewController/ViewController.swift | 93 ++++++++++++------- 13 files changed, 208 insertions(+), 114 deletions(-) diff --git a/GliaWidgets/Public/Glia/Glia+StartEngagement.swift b/GliaWidgets/Public/Glia/Glia+StartEngagement.swift index 616d4581c..ead149757 100644 --- a/GliaWidgets/Public/Glia/Glia+StartEngagement.swift +++ b/GliaWidgets/Public/Glia/Glia+StartEngagement.swift @@ -18,7 +18,7 @@ extension Glia { /// - `GliaError.engagementExists /// - `GliaError.sdkIsNotConfigured` /// - /// - Important: Note, that `configure(with:queueID:visitorContext:)` must be called initially prior to this method, + /// - Important: Note, that `configure(with:uiConfig:assetsBuilder:completion:)` must be called initially prior to this method, /// because `GliaError.sdkIsNotConfigured` will occur otherwise. /// public func startEngagement( @@ -28,7 +28,10 @@ extension Glia { features: Features = .all, sceneProvider: SceneProvider? = nil ) throws { - guard !queueIds.isEmpty else { throw GliaError.startingEngagementWithNoQueueIdsIsNotAllowed } + let trimmedQueueIds = queueIds + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .filter { !$0.isEmpty } + guard !trimmedQueueIds.isEmpty else { throw GliaError.startingEngagementWithNoQueueIdsIsNotAllowed } guard engagement == .none else { throw GliaError.engagementExists } guard let configuration = self.configuration else { throw GliaError.sdkIsNotConfigured } if let engagement = environment.coreSdk.getCurrentEngagement(), @@ -39,7 +42,7 @@ extension Glia { // Creates interactor instance let createdInteractor = setupInteractor( configuration: configuration, - queueIds: queueIds + queueIds: trimmedQueueIds ) theme.chat.connect.queue.firstText = companyName( diff --git a/GliaWidgets/Public/Glia/Glia.Deprecated.swift b/GliaWidgets/Public/Glia/Glia.Deprecated.swift index 5675af6f5..dda59f821 100644 --- a/GliaWidgets/Public/Glia/Glia.Deprecated.swift +++ b/GliaWidgets/Public/Glia/Glia.Deprecated.swift @@ -2,7 +2,7 @@ import GliaCoreSDK extension Glia { /// Deprecated. - @available(*, unavailable, message: "Use configure(with:queueId:uiConfig:assetsBuilder:completion:) instead.") + @available(*, unavailable, message: "Use configure(with:uiConfig:assetsBuilder:completion:) instead.") public func configure( with configuration: Configuration, queueId: String, @@ -184,6 +184,27 @@ extension Glia { sceneProvider: sceneProvider ) } + + /// Deprecated, use the `configure` method that provides a `Result` in its completion instead. + @available(*, deprecated, message: "Use the `configure` method that provides a `Result` in its completion instead.") + public func configure( + with configuration: Configuration, + uiConfig: RemoteConfiguration? = nil, + assetsBuilder: RemoteConfiguration.AssetsBuilder = .standard, + completion: (() -> Void)? = nil + ) throws { + try configure( + with: configuration, + uiConfig: uiConfig, + assetsBuilder: assetsBuilder + ) { result in + defer { + completion?() + } + guard case let .failure(error) = result else { return } + debugPrint("💥 Core SDK configuration is not valid. Unexpected error='\(error)'.") + } + } } extension Glia.Authentication { diff --git a/GliaWidgets/Public/Glia/Glia.RemoteConfiguration.swift b/GliaWidgets/Public/Glia/Glia.RemoteConfiguration.swift index 9341f171c..d4d42fb32 100644 --- a/GliaWidgets/Public/Glia/Glia.RemoteConfiguration.swift +++ b/GliaWidgets/Public/Glia/Glia.RemoteConfiguration.swift @@ -18,7 +18,7 @@ extension Glia { /// - `GliaError.engagementExists /// - `GliaError.sdkIsNotConfigured` /// - /// - Important: Note, that `configure(with:queueID:visitorContext:)` must be called initially prior to this method, + /// - Important: Note, that `configure(with:uiConfig:assetsBuilder:completion:)` must be called initially prior to this method, /// because `GliaError.sdkIsNotConfigured` will occur otherwise. /// public func startEngagementWithConfig( diff --git a/GliaWidgets/Public/Glia/Glia.swift b/GliaWidgets/Public/Glia/Glia.swift index 31ce3b2f6..f0ec3cbe8 100644 --- a/GliaWidgets/Public/Glia/Glia.swift +++ b/GliaWidgets/Public/Glia/Glia.swift @@ -104,23 +104,25 @@ public class Glia { /// Setup SDK using specific engagement configuration without starting the engagement. /// - Parameters: /// - configuration: Engagement configuration. - /// - visitorContext: Visitor context. /// - uiConfig: Remote UI configuration. /// - assetsBuilder: Provides assets for remote configuration. - /// - completion: Optional completion handler that will be fired once configuration is complete. - /// Passing `nil` will defer configuration. Passing closure will start configuration immediately. + /// - completion: Completion handler that will be fired once configuration is complete. public func configure( with configuration: Configuration, uiConfig: RemoteConfiguration? = nil, assetsBuilder: RemoteConfiguration.AssetsBuilder = .standard, - completion: (() -> Void)? = nil + completion: @escaping (Result) -> Void ) throws { guard environment.coreSdk.getCurrentEngagement() == nil else { throw GliaError.configuringDuringEngagementIsNotAllowed } self.uiConfig = uiConfig self.assetsBuilder = assetsBuilder - self.configuration = configuration + // `configuration` should be erased to avoid cases when integrators + // call `configure` and `startEngagement` asynchronously, and + // second-time configuration has not been complete, but `startEngagement` + // is fired and SDK has previous `configuration`. + self.configuration = nil self.callVisualizer.delegate = { action in switch action { @@ -129,10 +131,15 @@ public class Glia { } } - // TODO: - Non-optional completion will be added in MOB-2784 - do { - try environment.coreSDKConfigurator.configureWithConfiguration(configuration) { [weak self] in - guard let self else { return } + try environment.coreSDKConfigurator.configureWithConfiguration(configuration) { [weak self] result in + guard let self else { return } + switch result { + case .success: + // Storing `configuration` needs to be done once configuring SDK is complete + // Otherwise integrator can call `configure` and `startEngagement` + // asynchronously, without waiting configuration completion. + self.configuration = configuration + let getRemoteString = self.environment.coreSdk.localeProvider.getRemoteString self.stringProviding = .init(getRemoteString: getRemoteString) @@ -141,11 +148,11 @@ public class Glia { self.setupInteractor(configuration: configuration) } - completion?() + completion(.success(())) + case let .failure(error): + debugPrint("💥 Core SDK configuration is not valid. Unexpected error='\(error)'.") + completion(.failure(error)) } - } catch { - self.configuration = nil - debugPrint("💥 Core SDK configuration is not valid. Unexpected error='\(error)'.") } } @@ -208,11 +215,11 @@ public class Glia { /// - `GliaCoreSDK.ConfigurationError.invalidEnvironment` /// - `GliaError.sdkIsNotConfigured` /// - /// - Important: Note, that in case of engagement has not been started yet, `configure(with:queueID:visitorContext:)` must be called initially prior to this method, + /// - Important: Note, that in case of engagement has not been started yet, `configure(with:uiConfig:assetsBuilder:completion:)` must be called initially prior to this method, /// because `GliaError.sdkIsNotConfigured` will occur otherwise. /// public func fetchVisitorInfo(completion: @escaping (Result) -> Void) { - guard interactor != nil else { + guard configuration != nil else { completion(.failure(GliaError.sdkIsNotConfigured)) return } @@ -242,14 +249,14 @@ public class Glia { /// - `GliaCoreSDK.ConfigurationError.invalidEnvironment` /// - `GliaError.sdkIsNotConfigured` /// - /// - Important: Note, that in case of engagement has not been started yet, `configure(with:queueID:visitorContext:)` must be called initially prior to this method, + /// - Important: Note, that in case of engagement has not been started yet, `configure(with:uiConfig:assetsBuilder:completion:)` must be called initially prior to this method, /// because `GliaError.sdkIsNotConfigured` will occur otherwise. /// public func updateVisitorInfo( _ info: VisitorInfoUpdate, completion: @escaping (Result) -> Void ) { - guard interactor != nil else { + guard configuration != nil else { completion(.failure(GliaError.sdkIsNotConfigured)) return } @@ -263,7 +270,7 @@ public class Glia { rootCoordinator = nil } - guard interactor != nil else { + guard configuration != nil else { completion(.failure(GliaError.sdkIsNotConfigured)) return } @@ -278,10 +285,10 @@ public class Glia { /// It is also possible to monitor Queues changes with [subscribeForUpdates](x-source-tag://subscribeForUpdates) method. /// If the request is unsuccessful for any reason then the completion will have an Error. /// - Parameters: - /// - completion: A callback that will return the Result struct with `Queue` list or `GliaCoreError` + /// - completion: A callback that will return the Result struct with `Queue` list or `GliaCoreError`. /// public func listQueues(_ completion: @escaping (Result<[Queue], Error>) -> Void) { - guard interactor != nil else { + guard configuration != nil else { completion(.failure(GliaError.sdkIsNotConfigured)) return } diff --git a/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift b/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift index cb14acb1c..d734c0285 100644 --- a/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift +++ b/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift @@ -19,7 +19,7 @@ struct CoreSdkClient { typealias ConfigureWithConfiguration = ( _ sdkConfiguration: Self.Salemove.Configuration, - _ completion: (() -> Void)? + _ completion: @escaping Self.ConfigureCompletion ) -> Void var configureWithConfiguration: ConfigureWithConfiguration @@ -249,4 +249,5 @@ extension CoreSdkClient { typealias Cancellable = GliaCore.Cancellable typealias ReactiveSwift = GliaCoreDependency.ReactiveSwift typealias SendMessagePayload = GliaCoreSDK.SendMessagePayload + typealias ConfigureCompletion = GliaCoreSDK.GliaCore.ConfigureCompletion } diff --git a/GliaWidgets/Sources/CoreSDKConfigurator/CoreSDKConfigurator.Interface.swift b/GliaWidgets/Sources/CoreSDKConfigurator/CoreSDKConfigurator.Interface.swift index 80f5fed8d..3fa83ef96 100644 --- a/GliaWidgets/Sources/CoreSDKConfigurator/CoreSDKConfigurator.Interface.swift +++ b/GliaWidgets/Sources/CoreSDKConfigurator/CoreSDKConfigurator.Interface.swift @@ -2,7 +2,7 @@ import Foundation struct CoreSDKConfigurator { var configureWithInteractor: CoreSdkClient.ConfigureWithInteractor - var configureWithConfiguration: (Configuration, (() -> Void)?) throws -> Void + var configureWithConfiguration: (Configuration, @escaping (Result) -> Void) throws -> Void } extension CoreSDKConfigurator { @@ -16,9 +16,7 @@ extension CoreSDKConfigurator { authorizingMethod: configuration.authorizationMethod.coreAuthorizationMethod, pushNotifications: configuration.pushNotifications.coreSdk ) - coreSdk.configureWithConfiguration(sdkConfiguration) { - completion?() - } + coreSdk.configureWithConfiguration(sdkConfiguration, completion) } ) } diff --git a/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift b/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift index 3a0983729..c4f6cdf3f 100644 --- a/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift +++ b/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift @@ -86,7 +86,7 @@ extension CallViewController { let queueId = UUID.mock.uuidString var interactorEnv = Interactor.Environment.mock interactorEnv.coreSdk.configureWithConfiguration = { _, callback in - callback?() + callback(.success(())) } let interactor = Interactor.mock( queueId: queueId, diff --git a/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift b/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift index a4fb31855..d6ef76044 100644 --- a/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift +++ b/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift @@ -199,7 +199,7 @@ extension ChatViewController { chatViewModelEnv.fetchChatHistory = { $0(.success([])) } var interEnv = Interactor.Environment.mock interEnv.coreSdk.configureWithConfiguration = { _, callback in - callback?() + callback(.success(())) } let interactor = Interactor.mock(environment: interEnv) let generateUUID = UUID.incrementing @@ -540,7 +540,7 @@ extension ChatViewController { chatViewModelEnv.fetchChatHistory = { $0(.success(messages)) } var interEnv = Interactor.Environment.mock interEnv.coreSdk.configureWithConfiguration = { _, callback in - callback?() + callback(.success(())) } let interactor = Interactor.mock(environment: interEnv) let chatViewModel = ChatViewModel.mock(interactor: interactor, environment: chatViewModelEnv) diff --git a/GliaWidgetsTests/Sources/ChatViewModel/ChatViewModelTests.swift b/GliaWidgetsTests/Sources/ChatViewModel/ChatViewModelTests.swift index fd7479211..6b289fa2a 100644 --- a/GliaWidgetsTests/Sources/ChatViewModel/ChatViewModelTests.swift +++ b/GliaWidgetsTests/Sources/ChatViewModel/ChatViewModelTests.swift @@ -367,7 +367,7 @@ class ChatViewModelTests: XCTestCase { // we use the mocked one, which doesn't execute the completion, // `sendMessageWithAttachment` will not be executed. environment.coreSdk.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } let viewModel: ChatViewModel = .mock( @@ -595,7 +595,7 @@ class ChatViewModelTests: XCTestCase { let interactor = Interactor.failing interactor.environment.gcd.mainQueue.asyncIfNeeded = { $0() } interactor.environment.coreSdk.configureWithInteractor = { _ in } - interactor.environment.coreSdk.configureWithConfiguration = { _, callback in callback?() } + interactor.environment.coreSdk.configureWithConfiguration = { _, callback in callback(.success(())) } interactor.environment.coreSdk.sendMessagePreview = { _, _ in } interactor.environment.coreSdk.sendMessageWithMessagePayload = { _, completion in completion(.success(.mock(id: expectedMessageId))) @@ -645,7 +645,7 @@ class ChatViewModelTests: XCTestCase { let interactor = Interactor.failing interactor.environment.gcd.mainQueue.asyncIfNeeded = { $0() } interactor.environment.coreSdk.configureWithInteractor = { _ in } - interactor.environment.coreSdk.configureWithConfiguration = { _, callback in callback?() } + interactor.environment.coreSdk.configureWithConfiguration = { _, callback in callback(.success(())) } interactor.environment.coreSdk.sendMessagePreview = { _, _ in } interactor.environment.coreSdk.sendMessageWithMessagePayload = { [weak viewModel] _, completion in // Deliver message via socket before REST API response. diff --git a/GliaWidgetsTests/Sources/Glia/GliaTests+StartEngagement.swift b/GliaWidgetsTests/Sources/Glia/GliaTests+StartEngagement.swift index cd49506ad..babd2907d 100644 --- a/GliaWidgetsTests/Sources/Glia/GliaTests+StartEngagement.swift +++ b/GliaWidgetsTests/Sources/Glia/GliaTests+StartEngagement.swift @@ -11,7 +11,7 @@ extension GliaTests { func testStartEngagementThrowsErrorWhenEngagementAlreadyExists() throws { var sdkEnv = Glia.Environment.failing sdkEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } let sdk = Glia(environment: sdkEnv) sdk.rootCoordinator = .mock(engagementKind: .chat, screenShareHandler: .mock) @@ -30,9 +30,9 @@ extension GliaTests { func testStartEngagementThrowsErrorDuringActiveCallVisualizerEngagement() throws { let sdk = Glia(environment: .failing) sdk.environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } - try sdk.configure(with: .mock()) + try sdk.configure(with: .mock()) {} sdk.environment.coreSdk.getCurrentEngagement = { .mock(source: .callVisualizer) } XCTAssertThrowsError( @@ -67,7 +67,7 @@ extension GliaTests { } environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in calls.append(.configureWithConfiguration) - completion?() + completion(.success(())) } environment.coreSdk.localeProvider.getRemoteString = { _ in nil } environment.createRootCoordinator = { _, _, _, _, _, _, _ in @@ -99,7 +99,7 @@ extension GliaTests { ) } environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } environment.coreSDKConfigurator.configureWithInteractor = { _ in } environment.coreSdk.localeProvider.getRemoteString = { _ in nil } @@ -110,8 +110,13 @@ extension GliaTests { theme.call.connect.queue.firstText = "Glia 1" theme.chat.connect.queue.firstText = "Glia 2" - try sdk.configure(with: .mock()) - try sdk.startEngagement(engagementKind: .chat, in: ["queueId"], theme: theme) + try sdk.configure(with: .mock()) { + do { + try sdk.startEngagement(engagementKind: .chat, in: ["queueId"], theme: theme) + } catch { + XCTFail("startEngagement unexpectedly failed with error \(error), but should succeed instead.") + } + } let configuredSdkTheme = resultingViewFactory?.theme XCTAssertEqual(configuredSdkTheme?.call.connect.queue.firstText, "Glia 1") @@ -138,7 +143,7 @@ extension GliaTests { environment.coreSdk.localeProvider.getRemoteString = { _ in "Glia" } environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } environment.coreSDKConfigurator.configureWithInteractor = { _ in } environment.coreSdk.getCurrentEngagement = { nil } @@ -150,8 +155,13 @@ extension GliaTests { theme.call.connect.queue.firstText = "Glia 1" theme.chat.connect.queue.firstText = "Glia 2" - try sdk.configure(with: .mock()) { } - try sdk.startEngagement(engagementKind: .chat, in: ["queueId"], theme: theme) + try sdk.configure(with: .mock()) { + do { + try sdk.startEngagement(engagementKind: .chat, in: ["queueId"], theme: theme) + } catch { + XCTFail("startEngagement unexpectedly failed with error \(error), but should succeed instead.") + } + } let configuredSdkTheme = resultingViewFactory?.theme XCTAssertEqual(configuredSdkTheme?.call.connect.queue.firstText, "Glia") @@ -176,15 +186,20 @@ extension GliaTests { ) } environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } environment.coreSDKConfigurator.configureWithInteractor = { _ in } environment.coreSdk.localeProvider.getRemoteString = { _ in nil } let sdk = Glia(environment: environment) - try sdk.configure(with: .mock(companyName: "Glia")) - try sdk.startEngagement(engagementKind: .chat, in: ["queueId"]) + try sdk.configure(with: .mock(companyName: "Glia")) { + do { + try sdk.startEngagement(engagementKind: .chat, in: ["queueId"]) + } catch { + XCTFail("startEngagement unexpectedly failed with error \(error), but should succeed instead.") + } + } let configuredSdkTheme = resultingViewFactory?.theme XCTAssertEqual(configuredSdkTheme?.call.connect.queue.firstText, "Glia") @@ -209,15 +224,20 @@ extension GliaTests { ) } environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } environment.coreSDKConfigurator.configureWithInteractor = { _ in } environment.coreSdk.localeProvider.getRemoteString = { _ in nil } let sdk = Glia(environment: environment) - try sdk.configure(with: .mock()) - try sdk.startEngagement(engagementKind: .chat, in: ["queueId"]) + try sdk.configure(with: .mock()) { + do { + try sdk.startEngagement(engagementKind: .chat, in: ["queueId"]) + } catch { + XCTFail("startEngagement unexpectedly failed with error \(error), but should succeed instead.") + } + } let configuredSdkTheme = resultingViewFactory?.theme XCTAssertEqual(configuredSdkTheme?.call.connect.queue.firstText, "Company Name") @@ -244,7 +264,7 @@ extension GliaTests { environment.coreSdk.localeProvider.getRemoteString = { _ in "" } environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } environment.coreSDKConfigurator.configureWithInteractor = { _ in } environment.coreSdk.getCurrentEngagement = { nil } @@ -255,8 +275,13 @@ extension GliaTests { theme.call.connect.queue.firstText = "Glia 1" theme.chat.connect.queue.firstText = "Glia 2" - try sdk.configure(with: .mock()) - try sdk.startEngagement(engagementKind: .chat, in: ["queueId"], theme: theme) + try sdk.configure(with: .mock()) { + do { + try sdk.startEngagement(engagementKind: .chat, in: ["queueId"], theme: theme) + } catch { + XCTFail("startEngagement unexpectedly failed with error \(error), but should succeed instead.") + } + } let configuredSdkTheme = resultingViewFactory?.theme XCTAssertEqual(configuredSdkTheme?.call.connect.queue.firstText, "Glia 1") @@ -288,14 +313,14 @@ extension GliaTests { Glia.sharedInstance.stringProviding = StringProviding( getRemoteString: environment.coreSdk.localeProvider.getRemoteString ) - completion?() + completion(.success(())) } environment.coreSdk.getCurrentEngagement = { nil } let sdk = Glia(environment: environment) try sdk.configure(with: .mock(), completion: {}) try sdk.startEngagement(engagementKind: .chat, in: ["queueId"]) - + let configuredSdkTheme = resultingViewFactory?.theme let localFallbackCompanyName = "Company Name" XCTAssertEqual(configuredSdkTheme?.call.connect.queue.firstText, localFallbackCompanyName) diff --git a/GliaWidgetsTests/Sources/Glia/GliaTests.swift b/GliaWidgetsTests/Sources/Glia/GliaTests.swift index e4e3e83ef..25b7a9ebe 100644 --- a/GliaWidgetsTests/Sources/Glia/GliaTests.swift +++ b/GliaWidgetsTests/Sources/Glia/GliaTests.swift @@ -52,12 +52,11 @@ final class GliaTests: XCTestCase { gliaEnv.createFileUploadListModel = { _ in .mock() } gliaEnv.coreSdk.localeProvider.getRemoteString = { _ in nil } gliaEnv.coreSdk.authentication = { _ in .mock } - gliaEnv.coreSdk.configureWithConfiguration = { _, callback in callback?() } gliaEnv.coreSdk.queueForEngagement = { _, callback in callback(.success(.mock)) } gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } @@ -130,7 +129,7 @@ final class GliaTests: XCTestCase { var gliaEnv = Glia.Environment.failing gliaEnv.gcd.mainQueue.asyncIfNeeded = { callback in callback() } gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } @@ -138,7 +137,7 @@ final class GliaTests: XCTestCase { sdk.onEvent = { calls.append(.onEvent($0)) } - try sdk.configure(with: .mock()) + try sdk.configure(with: .mock()) {} sdk.callVisualizer.delegate?(.visitorCodeIsRequested) sdk.environment.coreSdk.getCurrentEngagement = { .mock(source: .callVisualizer) } @@ -158,7 +157,7 @@ final class GliaTests: XCTestCase { gliaEnv.coreSdk.configureWithConfiguration = { _, _ in } gliaEnv.gcd.mainQueue.asyncIfNeeded = { callback in callback() } gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } @@ -166,7 +165,7 @@ final class GliaTests: XCTestCase { sdk.onEvent = { calls.append(.onEvent($0)) } - try sdk.configure(with: .mock()) + try sdk.configure(with: .mock()) {} sdk.callVisualizer.delegate?(.visitorCodeIsRequested) sdk.environment.coreSdk.getCurrentEngagement = { .mock(source: .callVisualizer) } @@ -185,7 +184,7 @@ final class GliaTests: XCTestCase { gliaEnv.callVisualizerPresenter = .init(presenter: { nil }) gliaEnv.gcd.mainQueue.asyncIfNeeded = { callback in callback() } gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } @@ -193,7 +192,7 @@ final class GliaTests: XCTestCase { sdk.onEvent = { calls.append(.onEvent($0)) } - try sdk.configure(with: .mock()) + try sdk.configure(with: .mock()) {} sdk.callVisualizer.delegate?(.visitorCodeIsRequested) sdk.environment.coreSdk.getCurrentEngagement = { .mock(source: .callVisualizer) } @@ -220,7 +219,7 @@ final class GliaTests: XCTestCase { gliaEnv.gcd.mainQueue.asyncIfNeeded = { callback in callback() } gliaEnv.notificationCenter.addObserverClosure = { _, _, _, _ in } gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) } gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } @@ -228,7 +227,7 @@ final class GliaTests: XCTestCase { sdk.onEvent = { calls.append(.onEvent($0)) } - try sdk.configure(with: .mock()) + try sdk.configure(with: .mock()) {} sdk.callVisualizer.delegate?(.visitorCodeIsRequested) sdk.environment.coreSdk.getCurrentEngagement = { .mock(source: .callVisualizer) } @@ -243,7 +242,7 @@ final class GliaTests: XCTestCase { environment.coreSdk.getCurrentEngagement = { .mock() } let sdk = Glia(environment: environment) - XCTAssertThrowsError(try sdk.configure(with: .mock(), queueId: "queueId")) { error in + XCTAssertThrowsError(try sdk.configure(with: .mock()) {}) { error in XCTAssertEqual(error as? GliaError, GliaError.configuringDuringEngagementIsNotAllowed) } } @@ -304,27 +303,46 @@ final class GliaTests: XCTestCase { func test_isConfiguredIsTrueWhenConfigurationPerformed() throws { var environment = Glia.Environment.failing environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in - completion?() + completion(.success(())) + } + let sdk = Glia(environment: environment) + try sdk.configure(with: .mock()) {} + XCTAssertTrue(sdk.isConfigured) + } + + func test_isConfiguredIsFalseWhenSecondConfigureCallThrowsError() throws { + var environment = Glia.Environment.failing + var isFirstConfigure = true + environment.coreSDKConfigurator.configureWithConfiguration = { _, completion in + if isFirstConfigure { + isFirstConfigure = false + completion(.success(())) + } else { + throw CoreSdkClient.GliaCoreError.mock() + } } let sdk = Glia(environment: environment) - try sdk.configure(with: .mock()) + try sdk.configure(with: .mock()) {} XCTAssertTrue(sdk.isConfigured) + + try? sdk.configure(with: .mock()) {} + XCTAssertFalse(sdk.isConfigured) } - func test_isConfiguredIsFalseWhenConfigureWithConfigurationThrowsError() throws { + func test_isConfiguredIsFalseWhenConfigureWithConfigurationThrowsError() { var environment = Glia.Environment.failing environment.coreSDKConfigurator.configureWithConfiguration = { _, _ in throw CoreSdkClient.GliaCoreError.mock() } let sdk = Glia(environment: environment) - try sdk.configure(with: .mock()) + try? sdk.configure(with: .mock()) {} XCTAssertFalse(sdk.isConfigured) } func test_engagementCoordinatorGetsDeallocated() throws { var environment = Glia.Environment.failing environment.coreSDKConfigurator.configureWithConfiguration = { _, callback in - callback?() + callback(.success(())) } environment.coreSDKConfigurator.configureWithInteractor = { _ in } environment.coreSdk.localeProvider.getRemoteString = { _ in nil } @@ -363,7 +381,7 @@ final class GliaTests: XCTestCase { gliaEnv.screenShareHandler = screenShareHandler gliaEnv.gcd.mainQueue.asyncIfNeeded = { callback in callback() } gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, callback in - callback?() + callback(.success(())) } gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } let sdk = Glia(environment: gliaEnv) diff --git a/GliaWidgetsTests/Sources/InteractorTests.swift b/GliaWidgetsTests/Sources/InteractorTests.swift index c91c880e2..44da62bab 100644 --- a/GliaWidgetsTests/Sources/InteractorTests.swift +++ b/GliaWidgetsTests/Sources/InteractorTests.swift @@ -99,7 +99,7 @@ class InteractorTests: XCTestCase { var callbacks: [Callback] = [] var interactorEnv = Interactor.Environment.failing interactorEnv.coreSdk.configureWithInteractor = { _ in } - interactorEnv.coreSdk.configureWithConfiguration = { $1?() } + interactorEnv.coreSdk.configureWithConfiguration = { $1(.success(())) } interactorEnv.coreSdk.queueForEngagement = { _, _ in } interactorEnv.gcd = .mock let interactor = Interactor.mock(environment: interactorEnv) @@ -131,7 +131,7 @@ class InteractorTests: XCTestCase { var callbacks: [Callback] = [] var interactorEnv = Interactor.Environment.failing interactorEnv.coreSdk.configureWithInteractor = { _ in } - interactorEnv.coreSdk.configureWithConfiguration = { $1?() } + interactorEnv.coreSdk.configureWithConfiguration = { $1(.success(())) } interactorEnv.coreSdk.queueForEngagement = { _, completion in completion(.success(.mock)) } @@ -166,7 +166,7 @@ class InteractorTests: XCTestCase { var callbacks: [Callback] = [] var interactorEnv = Interactor.Environment.failing interactorEnv.coreSdk.configureWithInteractor = { _ in } - interactorEnv.coreSdk.configureWithConfiguration = { $1?() } + interactorEnv.coreSdk.configureWithConfiguration = { $1(.success(())) } interactorEnv.coreSdk.queueForEngagement = { _, completion in completion(.failure(.mock())) } @@ -380,7 +380,7 @@ class InteractorTests: XCTestCase { callbacks.append(.sendMessageWithAttachment) } interactorEnv.coreSdk.configureWithInteractor = { _ in } - interactorEnv.coreSdk.configureWithConfiguration = { $1?() } + interactorEnv.coreSdk.configureWithConfiguration = { $1(.success(())) } let interactor = Interactor.mock(environment: interactorEnv) interactor.send(messagePayload: .mock(content: "mock-message")) { result in diff --git a/TestingApp/ViewController/ViewController.swift b/TestingApp/ViewController/ViewController.swift index 2d28283ca..e90b66357 100644 --- a/TestingApp/ViewController/ViewController.swift +++ b/TestingApp/ViewController/ViewController.swift @@ -90,19 +90,22 @@ class ViewController: UIViewController { } private func showErrorAlert(using error: Error) { - guard let gliaError = error as? GliaError else { return } - - switch gliaError { - case GliaError.engagementExists: - alert(message: "Failed to start\nEngagement is ongoing, please use 'Resume' button") - case GliaError.engagementNotExist: - alert(message: "Failed to start\nNo ongoing engagement. Please start a new one with 'Start chat' button") - case GliaError.callVisualizerEngagementExists: - alert(message: "Failed to start\nCall Visualizer engagement is ongoing") - case GliaError.configuringDuringEngagementIsNotAllowed: - alert(message: "The operation couldn't be completed. '\(gliaError)'.") - default: - alert(message: "Failed to start\nCheck Glia parameters in Settings") + if let gliaError = error as? GliaError { + + switch gliaError { + case GliaError.engagementExists: + alert(message: "Failed to start\nEngagement is ongoing, please use 'Resume' button") + case GliaError.engagementNotExist: + alert(message: "Failed to start\nNo ongoing engagement. Please start a new one with 'Start chat' button") + case GliaError.callVisualizerEngagementExists: + alert(message: "Failed to start\nCall Visualizer engagement is ongoing") + case GliaError.configuringDuringEngagementIsNotAllowed: + alert(message: "The operation couldn't be completed. '\(gliaError)'.") + default: + alert(message: "Failed to start\nCheck Glia parameters in Settings") + } + } else { + alert(message: "Failed to execute with error: \(error)\nCheck Glia parameters in Settings") } } @@ -127,15 +130,18 @@ class ViewController: UIViewController { } @IBAction private func endEngagementTapped() { - self.catchingError { - // Since ending of engagement is possible - // only if such engagement exists, we need - // to configure SDK, and only then attempt - // to end engagement. - try Glia.sharedInstance.configure(with: configuration) { + // Since ending of engagement is possible + // only if such engagement exists, we need + // to configure SDK, and only then attempt + // to end engagement. + configureSDK(uiConfig: nil) { [weak self] result in + switch result { + case .success: Glia.sharedInstance.endEngagement { result in print("End engagement operation has been executed. Result='\(result)'.") } + case let .failure(error): + self?.showErrorAlert(using: error) } } } @@ -237,9 +243,15 @@ extension ViewController { do { try Glia.sharedInstance.configure( with: configuration - ) { - completionBlock("SDK has been configured") - completion?(.success(())) + ) { result in + switch result { + case .success: + completionBlock("SDK has been configured") + completion?(.success(())) + case let .failure(error): + completionBlock(error) + completion?(.failure(error)) + } } } catch { completionBlock(error) @@ -288,18 +300,30 @@ extension ViewController { } private func startEngagement(with kind: EngagementKind, config name: String) { - try? Glia.sharedInstance.configure( - with: configuration, - queueId: queueId - ) - guard let config = retrieveRemoteConfiguration(name) else { return } - try? Glia.sharedInstance.startEngagementWithConfig( - engagement: kind, - in: [queueId], - uiConfig: config - ) + let startEngagement = { + self.catchingError { + try Glia.sharedInstance.startEngagementWithConfig( + engagement: kind, + in: [self.queueId], + uiConfig: config + ) + } + } + + if autoConfigureSdkToggle.isOn { + configureSDK(uiConfig: nil) { [weak self] result in + switch result { + case .success: + startEngagement() + case let .failure(error): + self?.showErrorAlert(using: error) + } + } + } else { + startEngagement() + } } private func jsonNames() -> [String] { @@ -379,10 +403,7 @@ extension ViewController { @IBAction private func toggleAuthentication() { catchingError { - try Glia.sharedInstance.configure( - with: configuration, - queueId: queueId - ) { [weak self] in + try Glia.sharedInstance.configure(with: configuration) { [weak self] in guard let self = self else { return } self.catchingError { let authentication = try Self.authentication()