From 39b47be00da619bfceb1dd908efcc4da7d6631a0 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Tue, 4 Apr 2023 13:44:46 -0600 Subject: [PATCH 01/57] adds IterableDataRegion enum and data region value to IterableConfig --- swift-sdk/IterableConfig.swift | 3 +++ swift-sdk/IterableDataRegion.swift | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 swift-sdk/IterableDataRegion.swift diff --git a/swift-sdk/IterableConfig.swift b/swift-sdk/IterableConfig.swift index 20770bf45..0995d5eb0 100644 --- a/swift-sdk/IterableConfig.swift +++ b/swift-sdk/IterableConfig.swift @@ -124,4 +124,7 @@ public class IterableConfig: NSObject { /// Set whether the SDK should store in-apps only in memory, or in file storage public var useInMemoryStorageForInApps = false + + /// Sets data region which determines data center and endpoints used by the SDK + public var dataRegion: DataRegion = DataRegion.US } diff --git a/swift-sdk/IterableDataRegion.swift b/swift-sdk/IterableDataRegion.swift new file mode 100644 index 000000000..1c67ae82f --- /dev/null +++ b/swift-sdk/IterableDataRegion.swift @@ -0,0 +1,13 @@ +// +// File.swift +// +// +// Created by evan.greer@iterable.com on 4/4/23. +// + +import Foundation + +enum DataRegion { + static let US = "https://api.iterable.com" + static let EU = "https://api.eu.iterable.com" +} From adc31cbcb2f36291762a02e322e404fd55923512 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Wed, 5 Apr 2023 11:39:46 -0600 Subject: [PATCH 02/57] suggested updates and adds unit tests --- swift-sdk/Constants.swift | 5 ++++ swift-sdk/IterableConfig.swift | 2 +- swift-sdk/IterableDataRegion.swift | 13 ---------- tests/unit-tests/IterableConfigTests.swift | 30 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 14 deletions(-) delete mode 100644 swift-sdk/IterableDataRegion.swift create mode 100644 tests/unit-tests/IterableConfigTests.swift diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index 8c88fad38..eee5eca1a 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -270,6 +270,11 @@ enum JsonValue { } } +enum IterableDataRegion { + static let US = "https://api.iterable.com" + static let EU = "https://api.eu.iterable.com" +} + public protocol JsonValueRepresentable { var jsonValue: Any { get } } diff --git a/swift-sdk/IterableConfig.swift b/swift-sdk/IterableConfig.swift index 0995d5eb0..f97911ef8 100644 --- a/swift-sdk/IterableConfig.swift +++ b/swift-sdk/IterableConfig.swift @@ -126,5 +126,5 @@ public class IterableConfig: NSObject { public var useInMemoryStorageForInApps = false /// Sets data region which determines data center and endpoints used by the SDK - public var dataRegion: DataRegion = DataRegion.US + public var dataRegion: IterableDataRegion = IterableDataRegion.US } diff --git a/swift-sdk/IterableDataRegion.swift b/swift-sdk/IterableDataRegion.swift deleted file mode 100644 index 1c67ae82f..000000000 --- a/swift-sdk/IterableDataRegion.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// File.swift -// -// -// Created by evan.greer@iterable.com on 4/4/23. -// - -import Foundation - -enum DataRegion { - static let US = "https://api.iterable.com" - static let EU = "https://api.eu.iterable.com" -} diff --git a/tests/unit-tests/IterableConfigTests.swift b/tests/unit-tests/IterableConfigTests.swift new file mode 100644 index 000000000..f581d8ef2 --- /dev/null +++ b/tests/unit-tests/IterableConfigTests.swift @@ -0,0 +1,30 @@ +// +// File.swift +// +// +// Created by evan.greer@iterable.com on 4/5/23. +// + +import XCTest + +final class IterableConfigTests: XCTestCase { + func testDefaultConfig() { + let config = IterableConfig() + + XCTAssertEqual(config.pushIntegrationName, undefined) + XCTAssertEqual(config.sandboxPushIntegrationName, undefined) + XCTAssertEqual(config.pushPlatform, PushServicePlatform.auto) + XCTAssertEqual(config.urlDelegate, undefined) + XCTAssertEqual(config.customActionDelegate, undefined) + XCTAssertEqual(config.authDelegate, undefined) + XCTAssertEqual(config.autoPushRegistration, true) + XCTAssertEqual(config.checkForDeferredDeeplink, false) + XCTAssertEqual(config.logDelegate, DefaultLogDelegate()) + XCTAssertEqual(config.inAppDelegate, DefaultInAppDelegate()) + XCTAssertEqual(config.inAppDisplayInterval, 30.0) + XCTAssertEqual(config.expiringAuthTokenRefreshPeriod, 60.0) + XCTAssertEqual(config.allowedProtocols, []) + XCTAssertEqual(config.useInMemoryStorageForInApps, false) + XCTAssertEqual(config.dataRegion, IterableDataRegion.US) + } +} From b61e60996d27408e3f313b4b474e1bc23fcb8530 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Wed, 5 Apr 2023 16:20:49 -0600 Subject: [PATCH 03/57] removes IterableConfigTests --- swift-sdk/IterableConfig.swift | 2 +- tests/unit-tests/IterableConfigTests.swift | 30 ---------------------- 2 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 tests/unit-tests/IterableConfigTests.swift diff --git a/swift-sdk/IterableConfig.swift b/swift-sdk/IterableConfig.swift index f97911ef8..c254db7a9 100644 --- a/swift-sdk/IterableConfig.swift +++ b/swift-sdk/IterableConfig.swift @@ -126,5 +126,5 @@ public class IterableConfig: NSObject { public var useInMemoryStorageForInApps = false /// Sets data region which determines data center and endpoints used by the SDK - public var dataRegion: IterableDataRegion = IterableDataRegion.US + public var dataRegion: String = IterableDataRegion.US } diff --git a/tests/unit-tests/IterableConfigTests.swift b/tests/unit-tests/IterableConfigTests.swift deleted file mode 100644 index f581d8ef2..000000000 --- a/tests/unit-tests/IterableConfigTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// File.swift -// -// -// Created by evan.greer@iterable.com on 4/5/23. -// - -import XCTest - -final class IterableConfigTests: XCTestCase { - func testDefaultConfig() { - let config = IterableConfig() - - XCTAssertEqual(config.pushIntegrationName, undefined) - XCTAssertEqual(config.sandboxPushIntegrationName, undefined) - XCTAssertEqual(config.pushPlatform, PushServicePlatform.auto) - XCTAssertEqual(config.urlDelegate, undefined) - XCTAssertEqual(config.customActionDelegate, undefined) - XCTAssertEqual(config.authDelegate, undefined) - XCTAssertEqual(config.autoPushRegistration, true) - XCTAssertEqual(config.checkForDeferredDeeplink, false) - XCTAssertEqual(config.logDelegate, DefaultLogDelegate()) - XCTAssertEqual(config.inAppDelegate, DefaultInAppDelegate()) - XCTAssertEqual(config.inAppDisplayInterval, 30.0) - XCTAssertEqual(config.expiringAuthTokenRefreshPeriod, 60.0) - XCTAssertEqual(config.allowedProtocols, []) - XCTAssertEqual(config.useInMemoryStorageForInApps, false) - XCTAssertEqual(config.dataRegion, IterableDataRegion.US) - } -} From c4f1591c7642f18f366734ad1983c13c8f54e7ee Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Wed, 12 Apr 2023 08:31:04 -0600 Subject: [PATCH 04/57] adds getApiEndpoint function for redirecting --- .../swiftui-sample-app/IterableHelper.swift | 3 ++- swift-sdk/Internal/InternalIterableAPI.swift | 7 ++++++- swift-sdk/IterableAPI.swift | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift index c92e1c217..b41a8b4f9 100644 --- a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift +++ b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift @@ -8,7 +8,7 @@ import IterableSDK class IterableHelper { // Please replace with your API key #error("Please add your API Key here") - private static let apiKey = "" + private static let apiKey = "1d38cefe0f08466e8d87ac1e80363c82" static func initialize(launchOptions: [UIApplication.LaunchOptionsKey : Any]?) { let config = IterableConfig() @@ -16,6 +16,7 @@ class IterableHelper { // otherwise they will be deallocated config.urlDelegate = urlDelegate config.customActionDelegate = customActionDelegate + config.dataRegion = IterableDataRegion.EU IterableAPI.initialize(apiKey: apiKey, launchOptions: launchOptions, config: config) diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index 7ffe11008..6b4a9192b 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -555,6 +555,11 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { } } + private static func getApiEndpoint(apiEndPointOverride: String?, config: IterableConfig) -> String { + let apiEndPoint = config.dataRegion + return apiEndPointOverride ?? apiEndPoint + } + init(apiKey: String, launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil, config: IterableConfig = IterableConfig(), @@ -565,7 +570,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { self.apiKey = apiKey self.launchOptions = launchOptions self.config = config - apiEndPoint = apiEndPointOverride ?? Endpoint.api + apiEndPoint = InternalIterableAPI.getApiEndpoint(apiEndPointOverride: apiEndPointOverride, config: config) self.dependencyContainer = dependencyContainer dateProvider = dependencyContainer.dateProvider networkSession = dependencyContainer.networkSession diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 7dc73a0eb..3f5134d09 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -104,8 +104,9 @@ public final class IterableAPI: NSObject { launchOptions: [UIApplication.LaunchOptionsKey: Any]?, config: IterableConfig = IterableConfig()) { initialize2(apiKey: apiKey, - launchOptions: launchOptions, - config: config) + launchOptions: launchOptions, + config: config, + apiEndPointOverride: config.dataRegion) } /// DO NOT USE THIS. From 0c4b000861088bd1f733c8d013ff5739bd712310 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Thu, 13 Apr 2023 07:53:52 -0600 Subject: [PATCH 05/57] update IterableHelper in sample app --- .../swiftui-sample-app/swiftui-sample-app/IterableHelper.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift index b41a8b4f9..c92e1c217 100644 --- a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift +++ b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift @@ -8,7 +8,7 @@ import IterableSDK class IterableHelper { // Please replace with your API key #error("Please add your API Key here") - private static let apiKey = "1d38cefe0f08466e8d87ac1e80363c82" + private static let apiKey = "" static func initialize(launchOptions: [UIApplication.LaunchOptionsKey : Any]?) { let config = IterableConfig() @@ -16,7 +16,6 @@ class IterableHelper { // otherwise they will be deallocated config.urlDelegate = urlDelegate config.customActionDelegate = customActionDelegate - config.dataRegion = IterableDataRegion.EU IterableAPI.initialize(apiKey: apiKey, launchOptions: launchOptions, config: config) From d765e18e9b320a13350b4271e3ca3946bc99862c Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Thu, 13 Apr 2023 07:55:41 -0600 Subject: [PATCH 06/57] minor edits --- swift-sdk/IterableAPI.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 3f5134d09..98896584f 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -105,8 +105,7 @@ public final class IterableAPI: NSObject { config: IterableConfig = IterableConfig()) { initialize2(apiKey: apiKey, launchOptions: launchOptions, - config: config, - apiEndPointOverride: config.dataRegion) + config: config) } /// DO NOT USE THIS. From 391c6179e0024373fa4732d08b3fa9f771c61973 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Mon, 17 Apr 2023 13:32:49 -0700 Subject: [PATCH 07/57] minor edits --- swift-sdk/IterableAPI.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 98896584f..7dc73a0eb 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -104,8 +104,8 @@ public final class IterableAPI: NSObject { launchOptions: [UIApplication.LaunchOptionsKey: Any]?, config: IterableConfig = IterableConfig()) { initialize2(apiKey: apiKey, - launchOptions: launchOptions, - config: config) + launchOptions: launchOptions, + config: config) } /// DO NOT USE THIS. From 6a14f63500d4d6023ca53ad6b9db3e0f4fd54f9e Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Tue, 25 Apr 2023 21:11:29 +0530 Subject: [PATCH 08/57] Change for supporting callback feature on setEmail/setUserId --- swift-sdk/Internal/InternalIterableAPI.swift | 21 ++++++++++++++++---- swift-sdk/IterableAPI.swift | 8 ++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index ff8238240..69801b7c7 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -110,7 +110,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _payloadData = data } - func setEmail(_ email: String?, authToken: String? = nil) { + func setEmail(_ email: String?, authToken: String? = nil, resultCallback: ((Bool) -> Void)? = nil) { ITBInfo() if _email == email && email != nil && authToken != nil { @@ -126,13 +126,14 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _email = email _userId = nil + self.resultCallback = resultCallback storeIdentifierData() onLogin(authToken) } - func setUserId(_ userId: String?, authToken: String? = nil) { + func setUserId(_ userId: String?, authToken: String? = nil, resultCallback: ((Bool) -> Void)? = nil) { ITBInfo() if _userId == userId && userId != nil && authToken != nil { @@ -148,6 +149,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _email = nil _userId = userId + self.resultCallback = resultCallback storeIdentifierData() @@ -166,6 +168,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { guard let appName = pushIntegrationName else { let errorMessage = "Not registering device token - appName must not be nil" ITBError(errorMessage) + self.resultCallback?(false) onFailure?(errorMessage, nil) return } @@ -180,8 +183,15 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { sdkVersion: localStorage.sdkVersion) requestHandler.register(registerTokenInfo: registerTokenInfo, notificationStateProvider: notificationStateProvider, - onSuccess: onSuccess, - onFailure: onFailure) + onSuccess: { (_ data: [AnyHashable: Any]?) in + self.resultCallback?(true) + onSuccess?(data) + }, + onFailure: { (_ reason: String?, _ data: Data?) in + self.resultCallback?(false) + onFailure?(reason, data) + } + ) } @discardableResult @@ -410,6 +420,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { private var config: IterableConfig private var apiEndPoint: String + private var resultCallback: ((Bool) -> Void)? = nil /// Following are needed for handling pending notification and deep link. static var pendingNotificationResponse: NotificationResponseProtocol? @@ -536,6 +547,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { if config.autoPushRegistration { notificationStateProvider.registerForRemoteNotifications() + } else { + self.resultCallback?(true) } _ = inAppManager.scheduleSync() diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 6e49c64bb..83c7368c6 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -130,12 +130,12 @@ public final class IterableAPI: NSObject { // MARK: - SDK - public static func setEmail(_ email: String?, _ authToken: String? = nil) { - internalImplementation?.setEmail(email, authToken: authToken) + public static func setEmail(_ email: String?, _ authToken: String? = nil, _ successCallback: ((Bool) -> Void)? = nil) { + internalImplementation?.setEmail(email, authToken: authToken, resultCallback: resultCallback) } - public static func setUserId(_ userId: String?, _ authToken: String? = nil) { - internalImplementation?.setUserId(userId, authToken: authToken) + public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ successCallback: ((Bool) -> Void)? = nil) { + internalImplementation?.setUserId(userId, authToken: authToken, resultCallback: resultCallback) } /// Handle a Universal Link From 2b00983586c447b16c2156099335e78ac393a6cd Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Wed, 26 Apr 2023 14:41:49 +0530 Subject: [PATCH 09/57] add tests for setEmail/setUserId callback --- tests/unit-tests/IterableAPITests.swift | 61 +++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index edd7069a5..d572c9ca8 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -165,6 +165,67 @@ class IterableAPITests: XCTestCase { wait(for: [expectation], timeout: testExpectationTimeout) } + + func testSetEmailWithCallbackSuccess() { + let expectation = XCTestExpectation(description: "Set email with callback success") + + let config = IterableConfig() + let networkSession = MockNetworkSession(statusCode: 200) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: mockNetworkSession) + + internalAPI.setEmail("test@example.com") { error in + XCTAssertNil(error) + expectation.fulfill() + } + + wait(for: [expectation], timeout: testExpectationTimeout) + } + + func testSetEmailWithCallbackFailure() { + let expectation = XCTestExpectation(description: "Set email with callback failure") + + let config = IterableConfig() + let networkSession = MockNetworkSession(statusCode: 400, responseData: nil) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) + + internalAPI.setEmail("invalid_email") { error in + XCTAssertNotNil(error) + XCTAssertEqual(error?.localizedDescription, "Invalid email address") + expectation.fulfill() + } + + wait(for: [expectation], timeout: testExpectationTimeout) + } + + func testSetUserIdWithCallbackSuccess() { + let expectation = XCTestExpectation(description: "Set user ID with callback success") + + let config = IterableConfig() + let networkSession = MockNetworkSession(statusCode: 200) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: mockNetworkSession) + + internalAPI.setUserId("user123") { error in + XCTAssertNil(error) + expectation.fulfill() + } + + wait(for: [expectation], timeout: testExpectationTimeout) + } + + func testSetUserIdWithCallbackFailure() { + let expectation = XCTestExpectation(description: "Set user ID with callback failure") + + let config = IterableConfig() + let networkSession = MockNetworkSession(statusCode: 400) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: mockNetworkSession) + + internalAPI.setUserId("user123") { error in + XCTAssertNotNil(error) + expectation.fulfill() + } + + wait(for: [expectation], timeout: testExpectationTimeout) + } func testEmailPersistence() { let internalAPI = InternalIterableAPI.initializeForTesting() From 53c078f92b3d8fd1852fca0d7709bb16a7523a61 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Wed, 26 Apr 2023 15:12:10 +0530 Subject: [PATCH 10/57] Update IterableAPI.swift --- swift-sdk/IterableAPI.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 83c7368c6..face4219d 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -130,11 +130,11 @@ public final class IterableAPI: NSObject { // MARK: - SDK - public static func setEmail(_ email: String?, _ authToken: String? = nil, _ successCallback: ((Bool) -> Void)? = nil) { + public static func setEmail(_ email: String?, _ authToken: String? = nil, _ resultCallback: ((Bool) -> Void)? = nil) { internalImplementation?.setEmail(email, authToken: authToken, resultCallback: resultCallback) } - public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ successCallback: ((Bool) -> Void)? = nil) { + public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ resultCallback: ((Bool) -> Void)? = nil) { internalImplementation?.setUserId(userId, authToken: authToken, resultCallback: resultCallback) } From 7cc3d7b5715a1c863fee27b1e2441f0159f127e1 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Wed, 26 Apr 2023 15:42:29 +0530 Subject: [PATCH 11/57] Update IterableAPITests.swift --- tests/unit-tests/IterableAPITests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index d572c9ca8..31413a393 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -171,7 +171,7 @@ class IterableAPITests: XCTestCase { let config = IterableConfig() let networkSession = MockNetworkSession(statusCode: 200) - let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: mockNetworkSession) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) internalAPI.setEmail("test@example.com") { error in XCTAssertNil(error) @@ -185,7 +185,7 @@ class IterableAPITests: XCTestCase { let expectation = XCTestExpectation(description: "Set email with callback failure") let config = IterableConfig() - let networkSession = MockNetworkSession(statusCode: 400, responseData: nil) + let networkSession = MockNetworkSession(statusCode: 400) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) internalAPI.setEmail("invalid_email") { error in @@ -202,7 +202,7 @@ class IterableAPITests: XCTestCase { let config = IterableConfig() let networkSession = MockNetworkSession(statusCode: 200) - let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: mockNetworkSession) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) internalAPI.setUserId("user123") { error in XCTAssertNil(error) @@ -217,7 +217,7 @@ class IterableAPITests: XCTestCase { let config = IterableConfig() let networkSession = MockNetworkSession(statusCode: 400) - let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: mockNetworkSession) + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) internalAPI.setUserId("user123") { error in XCTAssertNotNil(error) From 7e4a7149c4d27ee0f9734e456f0a00a96f6afea0 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Thu, 27 Apr 2023 18:18:36 +0530 Subject: [PATCH 12/57] Add code for setRead/removeMessage callbacks --- swift-sdk/Constants.swift | 1 + swift-sdk/Internal/InAppManager.swift | 25 +++++++++++++------- swift-sdk/IterableInAppManagerProtocol.swift | 12 ++++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index 0e51a6e25..804d38e17 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -381,6 +381,7 @@ public enum IterableCustomActionName: String, CaseIterable { public typealias ITEActionBlock = (String?) -> Void public typealias ITBURLCallback = (URL?) -> Void +public typealias OnCompletionHandler = (Bool) -> Void public typealias OnSuccessHandler = (_ data: [AnyHashable: Any]?) -> Void public typealias OnFailureHandler = (_ reason: String?, _ data: Data?) -> Void public typealias UrlHandler = (URL) -> Bool diff --git a/swift-sdk/Internal/InAppManager.swift b/swift-sdk/Internal/InAppManager.swift index 130919faf..e5eee7359 100644 --- a/swift-sdk/Internal/InAppManager.swift +++ b/swift-sdk/Internal/InAppManager.swift @@ -121,15 +121,15 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { } } - func remove(message: IterableInAppMessage, location: InAppLocation) { + func remove(message: IterableInAppMessage, location: InAppLocation, onCompletion: OnCompletionHandler?) { ITBInfo() - + self.completionHandler = onCompletion removePrivate(message: message, location: location) } - func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource) { + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, onCompletion: OnCompletionHandler?) { ITBInfo() - + self.completionHandler = onCompletion removePrivate(message: message, location: location, source: source) } @@ -139,11 +139,15 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { removePrivate(message: message, location: location, source: source, inboxSessionId: inboxSessionId) } - func set(read: Bool, forMessage message: IterableInAppMessage) { + func set(read: Bool, forMessage message: IterableInAppMessage, onCompletion: OnCompletionHandler?) { + self.completionHandler = onCompletion updateMessage(message, read: read).onSuccess { [weak self] _ in + self?.completionHandler?(true) self?.callbackQueue.async { [weak self] in self?.notificationCenter.post(name: .iterableInboxChanged, object: self, userInfo: nil) } + }.onError { [weak self] _ in + self?.completionHandler?(false) } } @@ -183,7 +187,7 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { } } - func remove(message: IterableInAppMessage) { + func remove(message: IterableInAppMessage, onCompletion: OnCompletionHandler?) { ITBInfo() removePrivate(message: message) @@ -470,8 +474,12 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { location: location, source: source, inboxSessionId: inboxSessionId, - onSuccess: nil, - onFailure: nil) + onSuccess: { (_ data: [AnyHashable: Any]?) in + self.completionHandler?(true) + }, + onFailure: { (_ reason: String?, _ data: Data?) in + self.completionHandler?(false) + }) callbackQueue.async { [weak self] in self?.notificationCenter.post(name: .iterableInboxChanged, object: self, userInfo: nil) } @@ -533,6 +541,7 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { private var lastSyncTime: Date? private var moveToForegroundSyncInterval: Double = 1.0 * 60.0 // don't sync within sixty seconds private var autoDisplayPaused = false + private var completionHandler: OnCompletionHandler? = nil } extension InAppManager: InAppNotifiable { diff --git a/swift-sdk/IterableInAppManagerProtocol.swift b/swift-sdk/IterableInAppManagerProtocol.swift index d5a0ac9b2..906555678 100644 --- a/swift-sdk/IterableInAppManagerProtocol.swift +++ b/swift-sdk/IterableInAppManagerProtocol.swift @@ -29,20 +29,24 @@ import Foundation @objc(showMessage:consume:callbackBlock:) func show(message: IterableInAppMessage, consume: Bool, callback: ITBURLCallback?) /// - parameter message: The message to remove. - @objc(removeMessage:) func remove(message: IterableInAppMessage) + /// - parameter onCompletion: The callback which returns `success` or `failure`. + @objc(removeMessage:onCompletion:) func remove(message: IterableInAppMessage, onCompletion: OnCompletionHandler?) /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. - @objc(removeMessage:location:) func remove(message: IterableInAppMessage, location: InAppLocation) + /// - parameter onCompletion: The callback which returns `success` or `failure`. + @objc(removeMessage:location:onCompletion:) func remove(message: IterableInAppMessage, location: InAppLocation, onCompletion: OnCompletionHandler?) /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` - @objc(removeMessage:location:source:) func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource) + /// - parameter onCompletion: The callback which returns `success` or `failure`. + @objc(removeMessage:location:source:onCompletion:) func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, onCompletion: OnCompletionHandler?) /// - parameter read: Whether this inbox message was read /// - parameter message: The inbox message - @objc(setRead:forMessage:) func set(read: Bool, forMessage message: IterableInAppMessage) + /// - parameter onCompletion: The callback which returns `success` or `failure`. + @objc(setRead:forMessage:onCompletion:) func set(read: Bool, forMessage message: IterableInAppMessage, onCompletion: OnCompletionHandler?) /// - parameter id: The id of the message /// - returns: IterableInAppMessage with the id, if it exists. From 143b6fbdf9c510cf5e499cc8524d5ecb171cdcf1 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Thu, 27 Apr 2023 20:22:57 +0530 Subject: [PATCH 13/57] modify test for remove message --- tests/unit-tests/InboxTests.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/unit-tests/InboxTests.swift b/tests/unit-tests/InboxTests.swift index 37f98c535..aa54b7f32 100644 --- a/tests/unit-tests/InboxTests.swift +++ b/tests/unit-tests/InboxTests.swift @@ -162,12 +162,12 @@ class InboxTests: XCTestCase { let mockInAppFetcher = MockInAppFetcher() let config = IterableConfig() config.logDelegate = AllLogDelegate() - + let internalAPI = InternalIterableAPI.initializeForTesting( config: config, inAppFetcher: mockInAppFetcher ) - + let payload = """ {"inAppMessages": [ @@ -190,19 +190,21 @@ class InboxTests: XCTestCase { ] } """.toJsonDict() - + mockInAppFetcher.mockInAppPayloadFromServer(internalApi: internalAPI, payload).onSuccess { _ in let messages = internalAPI.inAppManager.getInboxMessages() XCTAssertEqual(messages.count, 2) - - internalAPI.inAppManager.remove(message: messages[0], location: .inbox, source: .inboxSwipe) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { + + let messageToRemove = messages[0] + internalAPI.inAppManager.remove(messageToRemove, location: .inbox, source: .inboxSwipe) { success in + XCTAssertTrue(success) let newMessages = internalAPI.inAppManager.getInboxMessages() XCTAssertEqual(newMessages.count, 1) + XCTAssertFalse(newMessages.contains(messageToRemove)) expectation1.fulfill() } } - + wait(for: [expectation1], timeout: testExpectationTimeout) } From 4f7d43479038181826ec910b79758620b257817a Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Fri, 12 May 2023 14:10:40 -0600 Subject: [PATCH 14/57] adds unit tests and slight refactor --- swift-sdk/Internal/InternalIterableAPI.swift | 12 ++++++--- tests/unit-tests/IterableAPITests.swift | 28 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index 6b4a9192b..755426857 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -78,6 +78,12 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { self.dependencyContainer.createAuthManager(config: self.config) }() + var apiEndPoint: String { + get { + _apiEndPoint + } + } + // MARK: - SDK Functions @discardableResult func handleUniversalLink(_ url: URL) -> Bool { @@ -400,7 +406,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { // MARK: - Private/Internal private var config: IterableConfig - private var apiEndPoint: String + private var _apiEndPoint: String /// Following are needed for handling pending notification and deep link. static var pendingNotificationResponse: NotificationResponseProtocol? @@ -555,7 +561,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { } } - private static func getApiEndpoint(apiEndPointOverride: String?, config: IterableConfig) -> String { + private static func setApiEndpoint(apiEndPointOverride: String?, config: IterableConfig) -> String { let apiEndPoint = config.dataRegion return apiEndPointOverride ?? apiEndPoint } @@ -570,7 +576,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { self.apiKey = apiKey self.launchOptions = launchOptions self.config = config - apiEndPoint = InternalIterableAPI.getApiEndpoint(apiEndPointOverride: apiEndPointOverride, config: config) + _apiEndPoint = InternalIterableAPI.setApiEndpoint(apiEndPointOverride: apiEndPointOverride, config: config) self.dependencyContainer = dependencyContainer dateProvider = dependencyContainer.dateProvider networkSession = dependencyContainer.networkSession diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index edd7069a5..2bb8a7659 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -11,6 +11,8 @@ class IterableAPITests: XCTestCase { private static let apiKey = "zeeApiKey" private static let email = "user@example.com" private static let userId = "testUserId" + private static let apiEndPointUS = "https://api.iterable.com" + private static let apiEndPointEU = "https://api.eu.iterable.com" override func setUp() { super.setUp() @@ -34,6 +36,32 @@ class IterableAPITests: XCTestCase { XCTAssertEqual(internalAPI.apiKey, IterableAPITests.apiKey) } + func testInitializeWithDefaultDataRegion() { + let prodIntegrationName = "the-best-app-ever" + + let config = IterableConfig() + config.pushIntegrationName = prodIntegrationName + config.inAppDisplayInterval = 1.0 + config.dataRegion = IterableDataRegion.US + + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config) + + XCTAssertEqual(internalAPI.apiEndPoint, IterableAPITests.apiEndPointUS) + } + + func testInitializeWithEUDataRegion() { + let prodIntegrationName = "the-best-app-ever" + + let config = IterableConfig() + config.pushIntegrationName = prodIntegrationName + config.inAppDisplayInterval = 1.0 + config.dataRegion = IterableDataRegion.EU + + let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config) + + XCTAssertEqual(internalAPI.apiEndPoint, IterableAPITests.apiEndPointEU) + } + func testInitializeCheckEndpoint() { let expectation1 = XCTestExpectation(description: "api endpoint called") From 5e55c03ebe0e4b6d526ba2e26f3356bb95a396e7 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Fri, 12 May 2023 18:21:16 -0600 Subject: [PATCH 15/57] public api endpoint rename --- .../swiftui-sample-app/IterableHelper.swift | 4 +++- swift-sdk/Internal/InternalIterableAPI.swift | 9 +++++---- tests/unit-tests/IterableAPITests.swift | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift index c92e1c217..29fb17cfa 100644 --- a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift +++ b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift @@ -8,7 +8,7 @@ import IterableSDK class IterableHelper { // Please replace with your API key #error("Please add your API Key here") - private static let apiKey = "" + private static let apiKey = "a8b1a3c7b5ec48119aa976f7f5a49603" static func initialize(launchOptions: [UIApplication.LaunchOptionsKey : Any]?) { let config = IterableConfig() @@ -16,6 +16,8 @@ class IterableHelper { // otherwise they will be deallocated config.urlDelegate = urlDelegate config.customActionDelegate = customActionDelegate + config.dataRegion = IterableDataRegion.EU + print("HIT!!!") IterableAPI.initialize(apiKey: apiKey, launchOptions: launchOptions, config: config) diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index 755426857..ef058d393 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -78,9 +78,9 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { self.dependencyContainer.createAuthManager(config: self.config) }() - var apiEndPoint: String { + var apiEndPointForTest: String { get { - _apiEndPoint + apiEndPoint } } @@ -406,7 +406,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { // MARK: - Private/Internal private var config: IterableConfig - private var _apiEndPoint: String + private var apiEndPoint: String /// Following are needed for handling pending notification and deep link. static var pendingNotificationResponse: NotificationResponseProtocol? @@ -576,7 +576,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { self.apiKey = apiKey self.launchOptions = launchOptions self.config = config - _apiEndPoint = InternalIterableAPI.setApiEndpoint(apiEndPointOverride: apiEndPointOverride, config: config) + apiEndPoint = InternalIterableAPI.setApiEndpoint(apiEndPointOverride: apiEndPointOverride, config: config) + print("api endpoint: " + apiEndPoint) self.dependencyContainer = dependencyContainer dateProvider = dependencyContainer.dateProvider networkSession = dependencyContainer.networkSession diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index 2bb8a7659..bfee61f83 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -46,7 +46,7 @@ class IterableAPITests: XCTestCase { let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config) - XCTAssertEqual(internalAPI.apiEndPoint, IterableAPITests.apiEndPointUS) + XCTAssertEqual(internalAPI.apiEndPointForTest, IterableAPITests.apiEndPointUS) } func testInitializeWithEUDataRegion() { @@ -59,7 +59,7 @@ class IterableAPITests: XCTestCase { let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config) - XCTAssertEqual(internalAPI.apiEndPoint, IterableAPITests.apiEndPointEU) + XCTAssertEqual(internalAPI.apiEndPointForTest, IterableAPITests.apiEndPointEU) } func testInitializeCheckEndpoint() { From 8b4a05dfb7212f25c4772e13282dfc60ace37e80 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Fri, 12 May 2023 18:25:44 -0600 Subject: [PATCH 16/57] reverts sample app --- .../swiftui-sample-app/IterableHelper.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift index 29fb17cfa..c92e1c217 100644 --- a/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift +++ b/sample-apps/swiftui-sample-app/swiftui-sample-app/IterableHelper.swift @@ -8,7 +8,7 @@ import IterableSDK class IterableHelper { // Please replace with your API key #error("Please add your API Key here") - private static let apiKey = "a8b1a3c7b5ec48119aa976f7f5a49603" + private static let apiKey = "" static func initialize(launchOptions: [UIApplication.LaunchOptionsKey : Any]?) { let config = IterableConfig() @@ -16,8 +16,6 @@ class IterableHelper { // otherwise they will be deallocated config.urlDelegate = urlDelegate config.customActionDelegate = customActionDelegate - config.dataRegion = IterableDataRegion.EU - print("HIT!!!") IterableAPI.initialize(apiKey: apiKey, launchOptions: launchOptions, config: config) From c66af11e4c5ee10f2f1004b5d56e97a5c7bec871 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Tue, 16 May 2023 10:27:42 -0600 Subject: [PATCH 17/57] removes print statement --- swift-sdk/Internal/InternalIterableAPI.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index ef058d393..16380a81e 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -577,7 +577,6 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { self.launchOptions = launchOptions self.config = config apiEndPoint = InternalIterableAPI.setApiEndpoint(apiEndPointOverride: apiEndPointOverride, config: config) - print("api endpoint: " + apiEndPoint) self.dependencyContainer = dependencyContainer dateProvider = dependencyContainer.dateProvider networkSession = dependencyContainer.networkSession From 97f22b07ddaec0095fc6940e2417a17e42fcdbca Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Thu, 18 May 2023 00:06:31 +0530 Subject: [PATCH 18/57] update code --- swift-sdk/Constants.swift | 1 + swift-sdk/Internal/InternalIterableAPI.swift | 19 ++++++++++--------- swift-sdk/IterableAPI.swift | 8 ++++---- tests/unit-tests/IterableAPITests.swift | 9 ++++----- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index 0e51a6e25..804d38e17 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -381,6 +381,7 @@ public enum IterableCustomActionName: String, CaseIterable { public typealias ITEActionBlock = (String?) -> Void public typealias ITBURLCallback = (URL?) -> Void +public typealias OnCompletionHandler = (Bool) -> Void public typealias OnSuccessHandler = (_ data: [AnyHashable: Any]?) -> Void public typealias OnFailureHandler = (_ reason: String?, _ data: Data?) -> Void public typealias UrlHandler = (URL) -> Bool diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index 69801b7c7..0c305a0bf 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -110,7 +110,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _payloadData = data } - func setEmail(_ email: String?, authToken: String? = nil, resultCallback: ((Bool) -> Void)? = nil) { + func setEmail(_ email: String?, authToken: String? = nil, onCompletion: OnCompletionHandler? = nil) { ITBInfo() if _email == email && email != nil && authToken != nil { @@ -126,14 +126,14 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _email = email _userId = nil - self.resultCallback = resultCallback + _resultCallback = onCompletion storeIdentifierData() onLogin(authToken) } - func setUserId(_ userId: String?, authToken: String? = nil, resultCallback: ((Bool) -> Void)? = nil) { + func setUserId(_ userId: String?, authToken: String? = nil, onCompletion: OnCompletionHandler? = nil) { ITBInfo() if _userId == userId && userId != nil && authToken != nil { @@ -149,7 +149,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _email = nil _userId = userId - self.resultCallback = resultCallback + _resultCallback = onCompletion storeIdentifierData() @@ -168,7 +168,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { guard let appName = pushIntegrationName else { let errorMessage = "Not registering device token - appName must not be nil" ITBError(errorMessage) - self.resultCallback?(false) + _resultCallback?(false) onFailure?(errorMessage, nil) return } @@ -184,11 +184,11 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { requestHandler.register(registerTokenInfo: registerTokenInfo, notificationStateProvider: notificationStateProvider, onSuccess: { (_ data: [AnyHashable: Any]?) in - self.resultCallback?(true) + _resultCallback?(true) onSuccess?(data) }, onFailure: { (_ reason: String?, _ data: Data?) in - self.resultCallback?(false) + _resultCallback?(false) onFailure?(reason, data) } ) @@ -420,7 +420,6 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { private var config: IterableConfig private var apiEndPoint: String - private var resultCallback: ((Bool) -> Void)? = nil /// Following are needed for handling pending notification and deep link. static var pendingNotificationResponse: NotificationResponseProtocol? @@ -438,6 +437,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { private var _email: String? private var _payloadData: [AnyHashable: Any]? private var _userId: String? + private var _resultCallback: OnCompletionHandler? = nil + /// the hex representation of this device token private var hexToken: String? @@ -548,7 +549,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { if config.autoPushRegistration { notificationStateProvider.registerForRemoteNotifications() } else { - self.resultCallback?(true) + _resultCallback?(true) } _ = inAppManager.scheduleSync() diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index face4219d..bd9848c5e 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -130,12 +130,12 @@ public final class IterableAPI: NSObject { // MARK: - SDK - public static func setEmail(_ email: String?, _ authToken: String? = nil, _ resultCallback: ((Bool) -> Void)? = nil) { - internalImplementation?.setEmail(email, authToken: authToken, resultCallback: resultCallback) + public static func setEmail(_ email: String?, _ authToken: String? = nil, _ onCompletion: OnCompletionHandler? = nil) { + internalImplementation?.setEmail(email, authToken: authToken, onCompletion: onCompletion) } - public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ resultCallback: ((Bool) -> Void)? = nil) { - internalImplementation?.setUserId(userId, authToken: authToken, resultCallback: resultCallback) + public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ onCompletion: OnCompletionHandler? = nil) { + internalImplementation?.setUserId(userId, authToken: authToken, onCompletion: onCompletion) } /// Handle a Universal Link diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index 31413a393..cf4eeed52 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -173,8 +173,8 @@ class IterableAPITests: XCTestCase { let networkSession = MockNetworkSession(statusCode: 200) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) - internalAPI.setEmail("test@example.com") { error in - XCTAssertNil(error) + internalAPI.setEmail("test@example.com") { success in + XCTAssertNotNil(success) expectation.fulfill() } @@ -190,7 +190,6 @@ class IterableAPITests: XCTestCase { internalAPI.setEmail("invalid_email") { error in XCTAssertNotNil(error) - XCTAssertEqual(error?.localizedDescription, "Invalid email address") expectation.fulfill() } @@ -204,8 +203,8 @@ class IterableAPITests: XCTestCase { let networkSession = MockNetworkSession(statusCode: 200) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) - internalAPI.setUserId("user123") { error in - XCTAssertNil(error) + internalAPI.setUserId("user123") { success in + XCTAssertNil(success) expectation.fulfill() } From ab62c996cb326764b3bd96dbbf113d4fdc3096e6 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Thu, 18 May 2023 01:09:15 +0530 Subject: [PATCH 19/57] Update InternalIterableAPI.swift --- swift-sdk/Internal/InternalIterableAPI.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index 5601daa0c..712d47501 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -185,11 +185,11 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { requestHandler.register(registerTokenInfo: registerTokenInfo, notificationStateProvider: notificationStateProvider, onSuccess: { (_ data: [AnyHashable: Any]?) in - _resultCallback?(true) + self._resultCallback?(true) onSuccess?(data) }, onFailure: { (_ reason: String?, _ data: Data?) in - _resultCallback?(false) + self._resultCallback?(false) onFailure?(reason, data) } ) From f68801fe5c071e74f1be206890cc5eae962c21f4 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Mon, 29 May 2023 21:24:57 +0530 Subject: [PATCH 20/57] fixes --- swift-sdk/Internal/EmptyInAppManager.swift | 11 ++--- swift-sdk/Internal/InAppManager.swift | 45 +++++++++----------- swift-sdk/IterableInAppManagerProtocol.swift | 20 +++++---- tests/unit-tests/InboxTests.swift | 21 ++++++--- 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/swift-sdk/Internal/EmptyInAppManager.swift b/swift-sdk/Internal/EmptyInAppManager.swift index 7f82d9569..9e907b430 100644 --- a/swift-sdk/Internal/EmptyInAppManager.swift +++ b/swift-sdk/Internal/EmptyInAppManager.swift @@ -6,6 +6,7 @@ import Foundation import UIKit class EmptyInAppManager: IterableInternalInAppManagerProtocol { + func start() -> Pending { Fulfill(value: true) } @@ -32,15 +33,15 @@ class EmptyInAppManager: IterableInternalInAppManagerProtocol { func show(message _: IterableInAppMessage, consume _: Bool, callback _: ITBURLCallback?) {} - func remove(message _: IterableInAppMessage) {} + func remove(message _: IterableInAppMessage, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} - func remove(message _: IterableInAppMessage, location _: InAppLocation) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} - func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} - func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource, inboxSessionId _: String?) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource, inboxSessionId _: String?, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} - func set(read _: Bool, forMessage _: IterableInAppMessage) {} + func set(read _: Bool, forMessage _: IterableInAppMessage, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} func getMessage(withId _: String) -> IterableInAppMessage? { nil diff --git a/swift-sdk/Internal/InAppManager.swift b/swift-sdk/Internal/InAppManager.swift index e5eee7359..cdb1e00cb 100644 --- a/swift-sdk/Internal/InAppManager.swift +++ b/swift-sdk/Internal/InAppManager.swift @@ -23,10 +23,13 @@ protocol IterableInternalInAppManagerProtocol: IterableInAppManagerProtocol, InA /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` /// - parameter inboxSessionId: The ID of the inbox session that the message originates from. - func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String?) + /// - parameter successHandler: The callback which returns `success. + /// - parameter failureHandler: The callback which returns `failure. + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String?, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) } class InAppManager: NSObject, IterableInternalInAppManagerProtocol { + init(requestHandler: RequestHandlerProtocol, deviceMetadata: DeviceMetadata, fetcher: InAppFetcherProtocol, @@ -121,33 +124,31 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { } } - func remove(message: IterableInAppMessage, location: InAppLocation, onCompletion: OnCompletionHandler?) { + func remove(message: IterableInAppMessage, location: InAppLocation, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { ITBInfo() - self.completionHandler = onCompletion - removePrivate(message: message, location: location) + removePrivate(message: message, location: location, successHandler: successHandler, failureHandler: failureHandler) } - func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, onCompletion: OnCompletionHandler?) { + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { ITBInfo() - self.completionHandler = onCompletion - removePrivate(message: message, location: location, source: source) + removePrivate(message: message, location: location, source: source, successHandler: successHandler, failureHandler: failureHandler) } - func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String? = nil) { + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String? = nil, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { ITBInfo() - removePrivate(message: message, location: location, source: source, inboxSessionId: inboxSessionId) + removePrivate(message: message, location: location, source: source, inboxSessionId: inboxSessionId, successHandler: successHandler, failureHandler: failureHandler) } - func set(read: Bool, forMessage message: IterableInAppMessage, onCompletion: OnCompletionHandler?) { - self.completionHandler = onCompletion + func set(read: Bool, forMessage message: IterableInAppMessage, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { updateMessage(message, read: read).onSuccess { [weak self] _ in - self?.completionHandler?(true) + var successObject: [AnyHashable: Any] = [:] + successHandler?(successObject) self?.callbackQueue.async { [weak self] in self?.notificationCenter.post(name: .iterableInboxChanged, object: self, userInfo: nil) } }.onError { [weak self] _ in - self?.completionHandler?(false) + failureHandler?(self?.description, nil) } } @@ -187,10 +188,10 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { } } - func remove(message: IterableInAppMessage, onCompletion: OnCompletionHandler?) { + func remove(message: IterableInAppMessage, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) { ITBInfo() - removePrivate(message: message) + removePrivate(message: message, location: .inApp, source: nil, successHandler: successHandler, failureHandler: failureHandler) } // MARK: - Private/Internal @@ -466,20 +467,17 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { private func removePrivate(message: IterableInAppMessage, location: InAppLocation = .inApp, source: InAppDeleteSource? = nil, - inboxSessionId: String? = nil) { + inboxSessionId: String? = nil, + successHandler: OnSuccessHandler? = nil, + failureHandler: OnFailureHandler? = nil) { ITBInfo() - updateMessage(message, didProcessTrigger: true, consumed: true) requestHandler?.inAppConsume(message: message, location: location, source: source, inboxSessionId: inboxSessionId, - onSuccess: { (_ data: [AnyHashable: Any]?) in - self.completionHandler?(true) - }, - onFailure: { (_ reason: String?, _ data: Data?) in - self.completionHandler?(false) - }) + onSuccess: successHandler, + onFailure: failureHandler) callbackQueue.async { [weak self] in self?.notificationCenter.post(name: .iterableInboxChanged, object: self, userInfo: nil) } @@ -541,7 +539,6 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { private var lastSyncTime: Date? private var moveToForegroundSyncInterval: Double = 1.0 * 60.0 // don't sync within sixty seconds private var autoDisplayPaused = false - private var completionHandler: OnCompletionHandler? = nil } extension InAppManager: InAppNotifiable { diff --git a/swift-sdk/IterableInAppManagerProtocol.swift b/swift-sdk/IterableInAppManagerProtocol.swift index 906555678..7c2796445 100644 --- a/swift-sdk/IterableInAppManagerProtocol.swift +++ b/swift-sdk/IterableInAppManagerProtocol.swift @@ -29,24 +29,28 @@ import Foundation @objc(showMessage:consume:callbackBlock:) func show(message: IterableInAppMessage, consume: Bool, callback: ITBURLCallback?) /// - parameter message: The message to remove. - /// - parameter onCompletion: The callback which returns `success` or `failure`. - @objc(removeMessage:onCompletion:) func remove(message: IterableInAppMessage, onCompletion: OnCompletionHandler?) + /// - parameter successHandler: The callback which returns `success. + /// - parameter failureHandler: The callback which returns `failure. + @objc(removeMessage:successHandler:failureHandler:) func remove(message: IterableInAppMessage, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. - /// - parameter onCompletion: The callback which returns `success` or `failure`. - @objc(removeMessage:location:onCompletion:) func remove(message: IterableInAppMessage, location: InAppLocation, onCompletion: OnCompletionHandler?) + /// - parameter successHandler: The callback which returns `success. + /// - parameter failureHandler: The callback which returns `failure. + @objc(removeMessage:location:successHandler:failureHandler:) func remove(message: IterableInAppMessage, location: InAppLocation, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` - /// - parameter onCompletion: The callback which returns `success` or `failure`. - @objc(removeMessage:location:source:onCompletion:) func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, onCompletion: OnCompletionHandler?) + /// - parameter successHandler: The callback which returns `success. + /// - parameter failureHandler: The callback which returns `failure. + @objc(removeMessage:location:source:successHandler:failureHandler:) func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) /// - parameter read: Whether this inbox message was read /// - parameter message: The inbox message - /// - parameter onCompletion: The callback which returns `success` or `failure`. - @objc(setRead:forMessage:onCompletion:) func set(read: Bool, forMessage message: IterableInAppMessage, onCompletion: OnCompletionHandler?) + /// - parameter successHandler: The callback which returns `success. + /// - parameter failureHandler: The callback which returns `failure. + @objc(setRead:forMessage:successHandler:failureHandler:) func set(read: Bool, forMessage message: IterableInAppMessage, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) /// - parameter id: The id of the message /// - returns: IterableInAppMessage with the id, if it exists. diff --git a/tests/unit-tests/InboxTests.swift b/tests/unit-tests/InboxTests.swift index aa54b7f32..e7612bc30 100644 --- a/tests/unit-tests/InboxTests.swift +++ b/tests/unit-tests/InboxTests.swift @@ -196,13 +196,20 @@ class InboxTests: XCTestCase { XCTAssertEqual(messages.count, 2) let messageToRemove = messages[0] - internalAPI.inAppManager.remove(messageToRemove, location: .inbox, source: .inboxSwipe) { success in - XCTAssertTrue(success) - let newMessages = internalAPI.inAppManager.getInboxMessages() - XCTAssertEqual(newMessages.count, 1) - XCTAssertFalse(newMessages.contains(messageToRemove)) - expectation1.fulfill() - } + internalAPI.inAppManager.remove( + message: messageToRemove, + location: .inbox, + source: .inboxSwipe, + successHandler: { _ in + // Success handler code + expectation1.fulfill() + }, + failureHandler: { _, _ in + // Failure handler code + XCTFail("Failed to remove message") + expectation1.fulfill() + } + ) } wait(for: [expectation1], timeout: testExpectationTimeout) From d37eccb193fb6afdbea667ff0bd7bc0b95617c85 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Mon, 29 May 2023 21:26:09 +0530 Subject: [PATCH 21/57] Update Constants.swift --- swift-sdk/Constants.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index 804d38e17..0e51a6e25 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -381,7 +381,6 @@ public enum IterableCustomActionName: String, CaseIterable { public typealias ITEActionBlock = (String?) -> Void public typealias ITBURLCallback = (URL?) -> Void -public typealias OnCompletionHandler = (Bool) -> Void public typealias OnSuccessHandler = (_ data: [AnyHashable: Any]?) -> Void public typealias OnFailureHandler = (_ reason: String?, _ data: Data?) -> Void public typealias UrlHandler = (URL) -> Bool From 97310608b1090a1cd2ac784951d5d717acd83f38 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Mon, 29 May 2023 21:48:10 +0530 Subject: [PATCH 22/57] fixes --- swift-sdk/Constants.swift | 1 - swift-sdk/Internal/InternalIterableAPI.swift | 21 ++++++++------ swift-sdk/IterableAPI.swift | 16 +++-------- tests/unit-tests/IterableAPITests.swift | 30 ++++++++++++++------ 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index 804d38e17..0e51a6e25 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -381,7 +381,6 @@ public enum IterableCustomActionName: String, CaseIterable { public typealias ITEActionBlock = (String?) -> Void public typealias ITBURLCallback = (URL?) -> Void -public typealias OnCompletionHandler = (Bool) -> Void public typealias OnSuccessHandler = (_ data: [AnyHashable: Any]?) -> Void public typealias OnFailureHandler = (_ reason: String?, _ data: Data?) -> Void public typealias UrlHandler = (URL) -> Bool diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index 712d47501..24e9be182 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -111,7 +111,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _payloadData = data } - func setEmail(_ email: String?, authToken: String? = nil, onCompletion: OnCompletionHandler? = nil) { + func setEmail(_ email: String?, authToken: String? = nil, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { ITBInfo() if _email == email && email != nil && authToken != nil { @@ -127,14 +127,15 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _email = email _userId = nil - _resultCallback = onCompletion + _successCallback = successHandler + _failureCallback = failureHandler storeIdentifierData() onLogin(authToken) } - func setUserId(_ userId: String?, authToken: String? = nil, onCompletion: OnCompletionHandler? = nil) { + func setUserId(_ userId: String?, authToken: String? = nil, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { ITBInfo() if _userId == userId && userId != nil && authToken != nil { @@ -150,7 +151,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { _email = nil _userId = userId - _resultCallback = onCompletion + _successCallback = successHandler + _failureCallback = failureHandler storeIdentifierData() @@ -169,7 +171,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { guard let appName = pushIntegrationName else { let errorMessage = "Not registering device token - appName must not be nil" ITBError(errorMessage) - _resultCallback?(false) + _failureCallback?(errorMessage, nil) onFailure?(errorMessage, nil) return } @@ -185,11 +187,11 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { requestHandler.register(registerTokenInfo: registerTokenInfo, notificationStateProvider: notificationStateProvider, onSuccess: { (_ data: [AnyHashable: Any]?) in - self._resultCallback?(true) + self._successCallback?(data) onSuccess?(data) }, onFailure: { (_ reason: String?, _ data: Data?) in - self._resultCallback?(false) + self._failureCallback?(reason, data) onFailure?(reason, data) } ) @@ -438,7 +440,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { private var _email: String? private var _payloadData: [AnyHashable: Any]? private var _userId: String? - private var _resultCallback: OnCompletionHandler? = nil + private var _successCallback: OnSuccessHandler? = nil + private var _failureCallback: OnFailureHandler? = nil /// the hex representation of this device token @@ -550,7 +553,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { if config.autoPushRegistration { notificationStateProvider.registerForRemoteNotifications() } else { - _resultCallback?(true) + _successCallback?([:]) } _ = inAppManager.scheduleSync() diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index e1c83371f..3ff15a6af 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -129,20 +129,12 @@ import UIKit // MARK: - SDK - public static func setEmail(_ email: String?, _ authToken: String? = nil, _ onCompletion: OnCompletionHandler? = nil) { - implementation?.setEmail(email, authToken: authToken, onCompletion: onCompletion) + public static func setEmail(_ email: String?, _ authToken: String? = nil, _ successHandler: OnSuccessHandler? = nil, _ failureHandler: OnFailureHandler? = nil) { + implementation?.setEmail(email, authToken: authToken, successHandler: successHandler, failureHandler: failureHandler) } - public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ onCompletion: OnCompletionHandler? = nil) { - implementation?.setUserId(userId, authToken: authToken, onCompletion: onCompletion) - } - - public static func setEmail(_ email: String?, _ authToken: String? = nil) { - implementation?.setEmail(email, authToken: authToken, onCompletion: nil) - } - - public static func setUserId(_ userId: String?, _ authToken: String? = nil) { - implementation?.setUserId(userId, authToken: authToken, onCompletion: nil) + public static func setUserId(_ userId: String?, _ authToken: String? = nil, _ successHandler: OnSuccessHandler? = nil, _ failureHandler: OnFailureHandler? = nil) { + implementation?.setUserId(userId, authToken: authToken, successHandler: successHandler, failureHandler: failureHandler) } /// Handle a Universal Link diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index 7cd7a60b6..75b113968 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -172,11 +172,14 @@ class IterableAPITests: XCTestCase { let config = IterableConfig() let networkSession = MockNetworkSession(statusCode: 200) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) - - internalAPI.setEmail("test@example.com") { success in + + internalAPI.setEmail("test@example.com", successHandler: { success in XCTAssertNotNil(success) expectation.fulfill() - } + }, failureHandler: { _, _ in + XCTFail("Failed to set email") + expectation.fulfill() + }) wait(for: [expectation], timeout: testExpectationTimeout) } @@ -188,10 +191,13 @@ class IterableAPITests: XCTestCase { let networkSession = MockNetworkSession(statusCode: 400) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) - internalAPI.setEmail("invalid_email") { error in + internalAPI.setEmail("invalid_email", successHandler: { success in + XCTFail("Email should not be set successfully") + expectation.fulfill() + }, failureHandler: { _, error in XCTAssertNotNil(error) expectation.fulfill() - } + }) wait(for: [expectation], timeout: testExpectationTimeout) } @@ -203,10 +209,13 @@ class IterableAPITests: XCTestCase { let networkSession = MockNetworkSession(statusCode: 200) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) - internalAPI.setUserId("user123") { success in + internalAPI.setUserId("user123", successHandler: { success in XCTAssertNil(success) expectation.fulfill() - } + }, failureHandler: { _, _ in + XCTFail("Failed to set user ID") + expectation.fulfill() + }) wait(for: [expectation], timeout: testExpectationTimeout) } @@ -218,10 +227,13 @@ class IterableAPITests: XCTestCase { let networkSession = MockNetworkSession(statusCode: 400) let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) - internalAPI.setUserId("user123") { error in + internalAPI.setUserId("user123", successHandler: { success in + XCTFail("User ID should not be set successfully") + expectation.fulfill() + }, failureHandler: { _, error in XCTAssertNotNil(error) expectation.fulfill() - } + }) wait(for: [expectation], timeout: testExpectationTimeout) } From 39e637e8b136a5122ca34546f7f65eea02c66707 Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Mon, 29 May 2023 21:52:24 +0530 Subject: [PATCH 23/57] fixes --- swift-sdk/Internal/InAppManager.swift | 3 +-- swift-sdk/Internal/InboxState.swift | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/swift-sdk/Internal/InAppManager.swift b/swift-sdk/Internal/InAppManager.swift index cdb1e00cb..36d710269 100644 --- a/swift-sdk/Internal/InAppManager.swift +++ b/swift-sdk/Internal/InAppManager.swift @@ -142,8 +142,7 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { func set(read: Bool, forMessage message: IterableInAppMessage, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { updateMessage(message, read: read).onSuccess { [weak self] _ in - var successObject: [AnyHashable: Any] = [:] - successHandler?(successObject) + successHandler?([:]) self?.callbackQueue.async { [weak self] in self?.notificationCenter.post(name: .iterableInboxChanged, object: self, userInfo: nil) } diff --git a/swift-sdk/Internal/InboxState.swift b/swift-sdk/Internal/InboxState.swift index dae2bea85..1524d109b 100644 --- a/swift-sdk/Internal/InboxState.swift +++ b/swift-sdk/Internal/InboxState.swift @@ -61,14 +61,16 @@ class InboxState: InboxStateProtocol { } func set(read: Bool, forMessage message: InboxMessageViewModel) { - inAppManager?.set(read: read, forMessage: message.iterableMessage) + inAppManager?.set(read: read, forMessage: message.iterableMessage, successHandler: nil, failureHandler: nil) } func remove(message: InboxMessageViewModel, inboxSessionId: String?) { inAppManager?.remove(message: message.iterableMessage, location: .inbox, source: .inboxSwipe, - inboxSessionId: inboxSessionId) + inboxSessionId: inboxSessionId, + successHandler: nil, + failureHandler: nil) } init(internalAPIProvider: @escaping @autoclosure () -> InternalIterableAPI? = IterableAPI.internalImplementation) { From 0ae380f3a01dae9e016614652763cc8f1965f7cd Mon Sep 17 00:00:00 2001 From: Hardik Mashru Date: Mon, 29 May 2023 22:13:34 +0530 Subject: [PATCH 24/57] fixes --- swift-sdk/Internal/EmptyInAppManager.swift | 10 ++++++ swift-sdk/Internal/InAppManager.swift | 38 +++++++++++++++++--- swift-sdk/Internal/InboxState.swift | 6 ++-- swift-sdk/IterableInAppManagerProtocol.swift | 19 ++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/swift-sdk/Internal/EmptyInAppManager.swift b/swift-sdk/Internal/EmptyInAppManager.swift index 9e907b430..26a584757 100644 --- a/swift-sdk/Internal/EmptyInAppManager.swift +++ b/swift-sdk/Internal/EmptyInAppManager.swift @@ -33,14 +33,24 @@ class EmptyInAppManager: IterableInternalInAppManagerProtocol { func show(message _: IterableInAppMessage, consume _: Bool, callback _: ITBURLCallback?) {} + func remove(message _: IterableInAppMessage) {} + func remove(message _: IterableInAppMessage, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource, inboxSessionId _: String?) {} + func remove(message _: IterableInAppMessage, location _: InAppLocation, source _: InAppDeleteSource, inboxSessionId _: String?, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} + func set(read _: Bool, forMessage _: IterableInAppMessage) {} + func set(read _: Bool, forMessage _: IterableInAppMessage, successHandler _: OnSuccessHandler?, failureHandler _: OnFailureHandler?) {} func getMessage(withId _: String) -> IterableInAppMessage? { diff --git a/swift-sdk/Internal/InAppManager.swift b/swift-sdk/Internal/InAppManager.swift index 36d710269..eff886a5c 100644 --- a/swift-sdk/Internal/InAppManager.swift +++ b/swift-sdk/Internal/InAppManager.swift @@ -19,6 +19,13 @@ protocol IterableInternalInAppManagerProtocol: IterableInAppManagerProtocol, InA /// - parameter inboxSessionId: The ID of the inbox session that the message originates from. func handleClick(clickedUrl url: URL?, forMessage message: IterableInAppMessage, location: InAppLocation, inboxSessionId: String?) + + /// - parameter message: The message to remove. + /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. + /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` + /// - parameter inboxSessionId: The ID of the inbox session that the message originates from. + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String?) + /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` @@ -124,22 +131,39 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { } } - func remove(message: IterableInAppMessage, location: InAppLocation, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { + func remove(message: IterableInAppMessage, location: InAppLocation) { ITBInfo() + + remove(message: message, location: location, successHandler: nil, failureHandler: nil) + } + + func remove(message: IterableInAppMessage, location: InAppLocation, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { removePrivate(message: message, location: location, successHandler: successHandler, failureHandler: failureHandler) } + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource) { + remove(message: message, location: location, source: source, successHandler: nil, failureHandler: nil) + } + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { - ITBInfo() + removePrivate(message: message, location: location, source: source, successHandler: successHandler, failureHandler: failureHandler) } - func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String? = nil, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String?) { ITBInfo() + remove(message: message, location: location, source: source, inboxSessionId: inboxSessionId, successHandler: nil, failureHandler: nil) + } + + func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, inboxSessionId: String? = nil, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { removePrivate(message: message, location: location, source: source, inboxSessionId: inboxSessionId, successHandler: successHandler, failureHandler: failureHandler) } + func set(read: Bool, forMessage message: IterableInAppMessage) { + set(read: read, forMessage: message, successHandler: nil, failureHandler: nil) + } + func set(read: Bool, forMessage message: IterableInAppMessage, successHandler: OnSuccessHandler? = nil, failureHandler: OnFailureHandler? = nil) { updateMessage(message, read: read).onSuccess { [weak self] _ in successHandler?([:]) @@ -187,9 +211,13 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol { } } - func remove(message: IterableInAppMessage, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) { + func remove(message: IterableInAppMessage) { ITBInfo() - + + remove(message: message, successHandler: nil, failureHandler: nil) + } + + func remove(message: IterableInAppMessage, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) { removePrivate(message: message, location: .inApp, source: nil, successHandler: successHandler, failureHandler: failureHandler) } diff --git a/swift-sdk/Internal/InboxState.swift b/swift-sdk/Internal/InboxState.swift index 1524d109b..dae2bea85 100644 --- a/swift-sdk/Internal/InboxState.swift +++ b/swift-sdk/Internal/InboxState.swift @@ -61,16 +61,14 @@ class InboxState: InboxStateProtocol { } func set(read: Bool, forMessage message: InboxMessageViewModel) { - inAppManager?.set(read: read, forMessage: message.iterableMessage, successHandler: nil, failureHandler: nil) + inAppManager?.set(read: read, forMessage: message.iterableMessage) } func remove(message: InboxMessageViewModel, inboxSessionId: String?) { inAppManager?.remove(message: message.iterableMessage, location: .inbox, source: .inboxSwipe, - inboxSessionId: inboxSessionId, - successHandler: nil, - failureHandler: nil) + inboxSessionId: inboxSessionId) } init(internalAPIProvider: @escaping @autoclosure () -> InternalIterableAPI? = IterableAPI.internalImplementation) { diff --git a/swift-sdk/IterableInAppManagerProtocol.swift b/swift-sdk/IterableInAppManagerProtocol.swift index 7c2796445..c4fd53569 100644 --- a/swift-sdk/IterableInAppManagerProtocol.swift +++ b/swift-sdk/IterableInAppManagerProtocol.swift @@ -28,17 +28,32 @@ import Foundation /// Note that this callback is called in addition to calling `IterableCustomActionDelegate` or `IterableUrlDelegate` on the button action. @objc(showMessage:consume:callbackBlock:) func show(message: IterableInAppMessage, consume: Bool, callback: ITBURLCallback?) + + /// - parameter message: The message to remove. + @objc(removeMessage:) func remove(message: IterableInAppMessage) + /// - parameter message: The message to remove. /// - parameter successHandler: The callback which returns `success. /// - parameter failureHandler: The callback which returns `failure. @objc(removeMessage:successHandler:failureHandler:) func remove(message: IterableInAppMessage, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) + + /// - parameter message: The message to remove. + /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. + @objc(removeMessage:location:) func remove(message: IterableInAppMessage, location: InAppLocation) + /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. /// - parameter successHandler: The callback which returns `success. /// - parameter failureHandler: The callback which returns `failure. @objc(removeMessage:location:successHandler:failureHandler:) func remove(message: IterableInAppMessage, location: InAppLocation, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) + + /// - parameter message: The message to remove. + /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. + /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` + @objc(removeMessage:location:source:) func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource) + /// - parameter message: The message to remove. /// - parameter location: The location from where this message was shown. `inbox` or `inApp`. /// - parameter source: The source of deletion `inboxSwipe` or `deleteButton`.` @@ -46,6 +61,10 @@ import Foundation /// - parameter failureHandler: The callback which returns `failure. @objc(removeMessage:location:source:successHandler:failureHandler:) func remove(message: IterableInAppMessage, location: InAppLocation, source: InAppDeleteSource, successHandler: OnSuccessHandler?, failureHandler: OnFailureHandler?) + /// - parameter read: Whether this inbox message was read + /// - parameter message: The inbox message + @objc(setRead:forMessage:) func set(read: Bool, forMessage message: IterableInAppMessage) + /// - parameter read: Whether this inbox message was read /// - parameter message: The inbox message /// - parameter successHandler: The callback which returns `success. From 34dbfb5a40de68e8d58b1499907c46d97dcc9d69 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Wed, 7 Jun 2023 09:40:43 -0700 Subject: [PATCH 25/57] endpoint fixes --- swift-sdk/Constants.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index eee5eca1a..f8c8996cc 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -271,8 +271,8 @@ enum JsonValue { } enum IterableDataRegion { - static let US = "https://api.iterable.com" - static let EU = "https://api.eu.iterable.com" + static let US = "https://api.iterable.com/api/" + static let EU = "https://api.eu.iterable.com/api/" } public protocol JsonValueRepresentable { From 418d174a25a2a375df1b2908c06cde30138cb539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Wed, 7 Jun 2023 14:10:58 -0700 Subject: [PATCH 26/57] [MOB - 6317] - Dataregion enum fix --- swift-sdk/Constants.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swift-sdk/Constants.swift b/swift-sdk/Constants.swift index f8c8996cc..0d5ef1d7b 100644 --- a/swift-sdk/Constants.swift +++ b/swift-sdk/Constants.swift @@ -270,9 +270,9 @@ enum JsonValue { } } -enum IterableDataRegion { - static let US = "https://api.iterable.com/api/" - static let EU = "https://api.eu.iterable.com/api/" +public enum IterableDataRegion { + public static let US = "https://api.iterable.com/api/" + public static let EU = "https://api.eu.iterable.com/api/" } public protocol JsonValueRepresentable { From 915fd04a9129ceb5a2f4e812246fc9f49a2a85ce Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Wed, 7 Jun 2023 16:32:59 -0700 Subject: [PATCH 27/57] updates unit tests --- tests/unit-tests/IterableAPITests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index bfee61f83..013b27a41 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -11,8 +11,8 @@ class IterableAPITests: XCTestCase { private static let apiKey = "zeeApiKey" private static let email = "user@example.com" private static let userId = "testUserId" - private static let apiEndPointUS = "https://api.iterable.com" - private static let apiEndPointEU = "https://api.eu.iterable.com" + private static let apiEndPointUS = "https://api.iterable.com/api/" + private static let apiEndPointEU = "https://api.eu.iterable.com/api/" override func setUp() { super.setUp() From 01d7e083448439984360e6401ec83253a6d082b6 Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Mon, 26 Jun 2023 10:59:05 -0700 Subject: [PATCH 28/57] Fixing tests --- tests/unit-tests/IterableAPITests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit-tests/IterableAPITests.swift b/tests/unit-tests/IterableAPITests.swift index 75b113968..079e489ea 100644 --- a/tests/unit-tests/IterableAPITests.swift +++ b/tests/unit-tests/IterableAPITests.swift @@ -180,7 +180,7 @@ class IterableAPITests: XCTestCase { XCTFail("Failed to set email") expectation.fulfill() }) - + internalAPI.register(token: "zeeToken".data(using: .utf8)!) wait(for: [expectation], timeout: testExpectationTimeout) } @@ -198,7 +198,7 @@ class IterableAPITests: XCTestCase { XCTAssertNotNil(error) expectation.fulfill() }) - + internalAPI.register(token: "zeeToken".data(using: .utf8)!) wait(for: [expectation], timeout: testExpectationTimeout) } @@ -210,13 +210,13 @@ class IterableAPITests: XCTestCase { let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: IterableAPITests.apiKey, config: config, networkSession: networkSession) internalAPI.setUserId("user123", successHandler: { success in - XCTAssertNil(success) + XCTAssertNotNil(success) expectation.fulfill() }, failureHandler: { _, _ in XCTFail("Failed to set user ID") expectation.fulfill() }) - + internalAPI.register(token: "zeeToken".data(using: .utf8)!) wait(for: [expectation], timeout: testExpectationTimeout) } @@ -234,7 +234,7 @@ class IterableAPITests: XCTestCase { XCTAssertNotNil(error) expectation.fulfill() }) - + internalAPI.register(token: "zeeToken".data(using: .utf8)!) wait(for: [expectation], timeout: testExpectationTimeout) } From ffe45e5e957a5b45370b173cccc57542e732b494 Mon Sep 17 00:00:00 2001 From: anlinguist Date: Thu, 6 Jul 2023 15:06:02 -0600 Subject: [PATCH 29/57] fix integration tests --- .github/workflows/e2e.yml | 1 + tests/endpoint-tests/CI.swift.template | 1 + tests/endpoint-tests/EndpointTests.swift | 14 +++++++++----- tests/endpoint-tests/Environment.swift | 5 +++++ tests/endpoint-tests/IterableAPISupport.swift | 5 +++-- tests/endpoint-tests/scripts/env_vars.sh.sample | 1 + tests/endpoint-tests/scripts/run_test.sh | 1 + 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c667834a0..9c6adf8a3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -16,6 +16,7 @@ jobs: - name: Build and test env: api_key: ${{secrets.E2E_API_KEY}} + server_api_key: ${{secrets.E2E_SERVER_API_KEY}} push_campaign_id: ${{secrets.E2E_PUSH_CAMPAIGN_ID}} push_template_id: ${{secrets.E2E_PUSH_TEMPLATE_ID}} in_app_campaign_id: ${{secrets.E2E_IN_APP_CAMPAIGN_ID}} diff --git a/tests/endpoint-tests/CI.swift.template b/tests/endpoint-tests/CI.swift.template index ab09c540a..575bfaab6 100644 --- a/tests/endpoint-tests/CI.swift.template +++ b/tests/endpoint-tests/CI.swift.template @@ -8,6 +8,7 @@ import Foundation /// Actual values will be injected by build step struct CI { static let apiKey = "{api_key}" + static let serverApiKey = "{server_api_key}" static let pushCampaignId = NSNumber(0) static let pushTemplateId = NSNumber(0) static let inAppCampaignId = NSNumber(0) diff --git a/tests/endpoint-tests/EndpointTests.swift b/tests/endpoint-tests/EndpointTests.swift index 5a8aa60f6..ffcf6fd9b 100644 --- a/tests/endpoint-tests/EndpointTests.swift +++ b/tests/endpoint-tests/EndpointTests.swift @@ -215,10 +215,13 @@ class EndpointTests: XCTestCase { ensureInAppMessages(api: api, email: email) api.inAppManager.scheduleSync().wait() - let count = api.inAppManager.getMessages().count - XCTAssert(count > 0) - - let message = api.inAppManager.getMessages()[0] + let messages = api.inAppManager.getMessages() + XCTAssert(messages.count > 0) + + guard let message = messages.first else { + XCTFail("No messages available") + return + } let startTime = Date() let endTime = startTime.addingTimeInterval(10.0) @@ -274,6 +277,7 @@ class EndpointTests: XCTestCase { } private static let apiKey = Environment.apiKey! + private static let serverApiKey = Environment.serverApiKey! private static let pushCampaignId = Environment.pushCampaignId! private static let pushTemplateId = Environment.pushTemplateId! private static let inAppCampaignId = Environment.inAppCampaignId! @@ -288,7 +292,7 @@ class EndpointTests: XCTestCase { } let expectation1 = expectation(for: predicate, evaluatedWith: nil, handler: nil) - wait(for: [expectation1], timeout: 60) + wait(for: [expectation1], timeout: 120) } private func clearAllInAppMessages(api: InternalIterableAPI) { diff --git a/tests/endpoint-tests/Environment.swift b/tests/endpoint-tests/Environment.swift index 3434aa380..f53a8f04a 100644 --- a/tests/endpoint-tests/Environment.swift +++ b/tests/endpoint-tests/Environment.swift @@ -10,6 +10,7 @@ import Foundation struct Environment { enum Key: String { case apiKey = "api_key" + case serverApiKey = "server_api_key" case pushCampaignId = "push_campaign_id" case pushTemplateId = "push_template_id" case inAppCampaignId = "in_app_campaign_id" @@ -19,6 +20,10 @@ struct Environment { static var apiKey: String? { getFromEnv(key: .apiKey) ?? CI.apiKey } + + static var serverApiKey: String? { + getFromEnv(key: .serverApiKey) ?? CI.serverApiKey + } static var pushCampaignId: NSNumber? { getNSNumberFromEnv(key: .pushCampaignId) ?? CI.pushCampaignId diff --git a/tests/endpoint-tests/IterableAPISupport.swift b/tests/endpoint-tests/IterableAPISupport.swift index 7630c59dd..2d52b44fb 100644 --- a/tests/endpoint-tests/IterableAPISupport.swift +++ b/tests/endpoint-tests/IterableAPISupport.swift @@ -12,7 +12,7 @@ struct IterableAPISupport { } var urlRequest = URLRequest(url: url) - urlRequest.setValue(apiKey, forHTTPHeaderField: JsonKey.Header.apiKey) + urlRequest.setValue(serverApiKey, forHTTPHeaderField: JsonKey.Header.apiKey) return RequestSender.sendRequest(urlRequest, usingSession: urlSession) } @@ -44,6 +44,7 @@ struct IterableAPISupport { } private static let apiKey = Environment.apiKey! + private static let serverApiKey = Environment.serverApiKey! private static func createPostRequest(iterablePostRequest: PostRequest) -> URLRequest? { IterableRequestUtil.createPostRequest(forApiEndPoint: Path.apiEndpoint, @@ -57,7 +58,7 @@ struct IterableAPISupport { [JsonKey.contentType: JsonValue.applicationJson, JsonKey.Header.sdkPlatform: JsonValue.iOS, JsonKey.Header.sdkVersion: IterableAPI.sdkVersion, - JsonKey.Header.apiKey: apiKey] + JsonKey.Header.apiKey: serverApiKey] } private static var urlSession: URLSession = { diff --git a/tests/endpoint-tests/scripts/env_vars.sh.sample b/tests/endpoint-tests/scripts/env_vars.sh.sample index 4bb20b8de..aa6cd0455 100755 --- a/tests/endpoint-tests/scripts/env_vars.sh.sample +++ b/tests/endpoint-tests/scripts/env_vars.sh.sample @@ -1,6 +1,7 @@ #!/bin/bash export api_key=the_api_key +export server_api_key=the_server_api_key export push_campaign_id=0 export push_template_id=0 export in_app_campaign_id=0 diff --git a/tests/endpoint-tests/scripts/run_test.sh b/tests/endpoint-tests/scripts/run_test.sh index 0b7a6ce79..5556f767d 100755 --- a/tests/endpoint-tests/scripts/run_test.sh +++ b/tests/endpoint-tests/scripts/run_test.sh @@ -14,6 +14,7 @@ fi echo Generating CI.swift sed -e "s/\(apiKey = \).*$/\1\"$api_key\"/" \ +-e "s/\(serverApiKey = \).*$/\1\"$server_api_key\"/" \ -e "s/\(pushCampaignId = \).*$/\1\NSNumber($push_campaign_id)/" \ -e "s/\(pushTemplateId = \).*$/\1\NSNumber($push_template_id)/" \ -e "s/\(inAppCampaignId = \).*$/\1\NSNumber($in_app_campaign_id)/" \ From fa4fc2d73a6a98cdaff14b583875aa806aa84d20 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Thu, 6 Jul 2023 18:05:32 -0600 Subject: [PATCH 30/57] fixes testRemove unit test --- tests/unit-tests/InboxTests.swift | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/unit-tests/InboxTests.swift b/tests/unit-tests/InboxTests.swift index e7612bc30..79f466015 100644 --- a/tests/unit-tests/InboxTests.swift +++ b/tests/unit-tests/InboxTests.swift @@ -194,22 +194,21 @@ class InboxTests: XCTestCase { mockInAppFetcher.mockInAppPayloadFromServer(internalApi: internalAPI, payload).onSuccess { _ in let messages = internalAPI.inAppManager.getInboxMessages() XCTAssertEqual(messages.count, 2) - + let messageToRemove = messages[0] internalAPI.inAppManager.remove( message: messageToRemove, location: .inbox, source: .inboxSwipe, - successHandler: { _ in - // Success handler code - expectation1.fulfill() - }, - failureHandler: { _, _ in - // Failure handler code - XCTFail("Failed to remove message") - expectation1.fulfill() - } + successHandler: { _ in }, + failureHandler: { _, _ in } ) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { + let newMessages = internalAPI.inAppManager.getInboxMessages() + XCTAssertEqual(newMessages.count, 1) + expectation1.fulfill() + } } wait(for: [expectation1], timeout: testExpectationTimeout) From 02f5d48434364f1830615b030d6eedc85630b046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Fri, 7 Jul 2023 11:12:00 -0700 Subject: [PATCH 31/57] [MOB - 6480] - Prepare for release 6.4.14 --- CHANGELOG.md | 5 +++++ Iterable-iOS-AppExtensions.podspec | 2 +- Iterable-iOS-SDK.podspec | 2 +- swift-sdk/IterableAPI.swift | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7be5eeb6d..a547ddf87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.4.14 +### Added +- Success and Failure handlers can now be passed to following functions: +`InAppManager.remove`, `InAppManager.setRead`, `IterableAPI.setEmail` and `IterableAPI.setUserId` + ## 6.4.13 ### Added - `ITBNotificationServiceExtension` has a new optional delegate in the scenario of wanting to receive and pass along push information (e.g. Firebase) diff --git a/Iterable-iOS-AppExtensions.podspec b/Iterable-iOS-AppExtensions.podspec index c8f405671..4dd252b63 100644 --- a/Iterable-iOS-AppExtensions.podspec +++ b/Iterable-iOS-AppExtensions.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-AppExtensions" s.module_name = "IterableAppExtensions" - s.version = "6.4.13" + s.version = "6.4.14" s.summary = "App Extensions for Iterable SDK" s.description = <<-DESC diff --git a/Iterable-iOS-SDK.podspec b/Iterable-iOS-SDK.podspec index c3ca66e47..48c4108dd 100644 --- a/Iterable-iOS-SDK.podspec +++ b/Iterable-iOS-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-SDK" s.module_name = "IterableSDK" - s.version = "6.4.13" + s.version = "6.4.14" s.summary = "Iterable's official SDK for iOS" s.description = <<-DESC diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 3ff15a6af..72ae6edd7 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -7,7 +7,7 @@ import UIKit @objcMembers public final class IterableAPI: NSObject { /// The current SDK version - public static let sdkVersion = "6.4.13" + public static let sdkVersion = "6.4.14" /// The email of the logged in user that this IterableAPI is using public static var email: String? { From 0e2c96848e2acbf8f1e2c58b8836715e229682e9 Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Mon, 24 Jul 2023 13:24:30 -0600 Subject: [PATCH 32/57] updates podspec and changelog --- CHANGELOG.md | 4 ++++ Iterable-iOS-AppExtensions.podspec | 2 +- Iterable-iOS-SDK.podspec | 2 +- swift-sdk/IterableAPI.swift | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a547ddf87..8286c6c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.4.15 +### Added +- `config.dataRegion` which configures the iOS SDK to use API endpoints for the Iterable EU data center. This configuration defaults to API endpoints for the US data center. + ## 6.4.14 ### Added - Success and Failure handlers can now be passed to following functions: diff --git a/Iterable-iOS-AppExtensions.podspec b/Iterable-iOS-AppExtensions.podspec index 4dd252b63..c1f0c8d96 100644 --- a/Iterable-iOS-AppExtensions.podspec +++ b/Iterable-iOS-AppExtensions.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-AppExtensions" s.module_name = "IterableAppExtensions" - s.version = "6.4.14" + s.version = "6.4.15" s.summary = "App Extensions for Iterable SDK" s.description = <<-DESC diff --git a/Iterable-iOS-SDK.podspec b/Iterable-iOS-SDK.podspec index 48c4108dd..57d3fbab6 100644 --- a/Iterable-iOS-SDK.podspec +++ b/Iterable-iOS-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-SDK" s.module_name = "IterableSDK" - s.version = "6.4.14" + s.version = "6.4.15" s.summary = "Iterable's official SDK for iOS" s.description = <<-DESC diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 72ae6edd7..9c57fd4b3 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -7,7 +7,7 @@ import UIKit @objcMembers public final class IterableAPI: NSObject { /// The current SDK version - public static let sdkVersion = "6.4.14" + public static let sdkVersion = "6.4.15" /// The email of the logged in user that this IterableAPI is using public static var email: String? { From ca3dba81d84f44c9cb0ea32c44ef9d5cd602bfbf Mon Sep 17 00:00:00 2001 From: "evan.greer@iterable.com" Date: Mon, 24 Jul 2023 14:07:55 -0600 Subject: [PATCH 33/57] updates changelog --- CHANGELOG.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8286c6c1c..bcab5547a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,23 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 6.4.15 ### Added -- `config.dataRegion` which configures the iOS SDK to use API endpoints for the Iterable EU data center. This configuration defaults to API endpoints for the US data center. +- This release allows you to use projects hosted on Iterable's EU data center. If your project is hosted on Iterable's [European data center (EUDC)](https://support.iterable.com/hc/articles/17572750887444), configure the SDK to use Iterable's EU-based API endpoints: + +_Swift_ + +```swift +let config = IterableConfig() +config.dataRegion = IterableDataRegion.EU +IterableAPI.initialize(apiKey: "", launchOptions: launchOptions, config: config) +``` + +_Objective-C_ + +```objectivec +IterableConfig *config = [[IterableConfig alloc] init]; +config.dataRegion = IterableDataRegion.EU; +[IterableAPI initializeWithApiKey:@"" launchOptions:launchOptions config:config]; +``` ## 6.4.14 ### Added From 275f4cff5823313d9ebb275c72cade9205f17b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Thu, 27 Jul 2023 16:05:33 -0700 Subject: [PATCH 34/57] No Offline Components when Online-Only 1. Turned the default value to false instead of true. Only once the response comes back as true, the value will be set to true. Subsequent launches will respect whats stored after that. There will not be a toggling happening every now and then. Once the response from server is received as true, its stored in the sdk. 2. Create RequestHandler initializes both online and offline request Handler irrespective of the feature enabled or disabled. Added lazy var for offlineprocessor and initializing offline componenents only when offlineMode is true. This change will make sure that components like TaskSchedular and HealthMonitor will not be even initialized unless offlineMode is true. However, this change does not engage offlineMode immediately after the response is received as true as creation of request handler has already passed and only a fresh launch of app can then go through offline request processor. Additional changes are needed to make sure offlineMode when accessed, initializes its component and not when requestHandler is initialized. That will keep the offline component encapsulated within offline flow. --- .../DependencyContainerProtocol.swift | 23 +++++++++++++------ swift-sdk/Internal/RequestHandler.swift | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/swift-sdk/Internal/DependencyContainerProtocol.swift b/swift-sdk/Internal/DependencyContainerProtocol.swift index e5754d8d6..2a1e9b434 100644 --- a/swift-sdk/Internal/DependencyContainerProtocol.swift +++ b/swift-sdk/Internal/DependencyContainerProtocol.swift @@ -70,22 +70,31 @@ extension DependencyContainerProtocol { networkSession: networkSession, deviceMetadata: deviceMetadata, dateProvider: dateProvider) - if let persistenceContextProvider = createPersistenceContextProvider() { - let healthMonitorDataProvider = createHealthMonitorDataProvider(persistenceContextProvider: persistenceContextProvider) - let healthMonitor = HealthMonitor(dataProvider: healthMonitorDataProvider, + lazy var offlineProcessor:OfflineRequestProcessor? = nil + lazy var healthMonitor: HealthMonitor? = nil + + + if(offlineMode) { + if let persistenceContextProvider = createPersistenceContextProvider() { + + let healthMonitorDataProvider = createHealthMonitorDataProvider(persistenceContextProvider: persistenceContextProvider) + + healthMonitor = HealthMonitor(dataProvider: healthMonitorDataProvider, dateProvider: dateProvider, networkSession: networkSession) - let offlineProcessor = OfflineRequestProcessor(apiKey: apiKey, + offlineProcessor = OfflineRequestProcessor(apiKey: apiKey, authProvider: authProvider, authManager: authManager, endpoint: endpoint, deviceMetadata: deviceMetadata, taskScheduler: createTaskScheduler(persistenceContextProvider: persistenceContextProvider, - healthMonitor: healthMonitor), + healthMonitor: healthMonitor!), taskRunner: createTaskRunner(persistenceContextProvider: persistenceContextProvider, - healthMonitor: healthMonitor), + healthMonitor: healthMonitor!), notificationCenter: notificationCenter) - return RequestHandler(onlineProcessor: onlineProcessor, + } + + return RequestHandler(onlineProcessor: onlineProcessor, offlineProcessor: offlineProcessor, healthMonitor: healthMonitor, offlineMode: offlineMode) diff --git a/swift-sdk/Internal/RequestHandler.swift b/swift-sdk/Internal/RequestHandler.swift index f395b725a..b56a2f18d 100644 --- a/swift-sdk/Internal/RequestHandler.swift +++ b/swift-sdk/Internal/RequestHandler.swift @@ -8,7 +8,7 @@ class RequestHandler: RequestHandlerProtocol { init(onlineProcessor: OnlineRequestProcessor, offlineProcessor: OfflineRequestProcessor?, healthMonitor: HealthMonitor?, - offlineMode: Bool = true) { + offlineMode: Bool = false) { ITBInfo() self.onlineProcessor = onlineProcessor self.offlineProcessor = offlineProcessor From 7ae36049236ac16f7264680fd03f9ca9f834a032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Tue, 1 Aug 2023 00:34:14 -0700 Subject: [PATCH 35/57] Ignoring a test Ignoring a test as it removes the realtime switching of offline functionality and rather defers it for later session. --- .../xcshareddata/xcschemes/swift-sdk.xcscheme | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swift-sdk.xcodeproj/xcshareddata/xcschemes/swift-sdk.xcscheme b/swift-sdk.xcodeproj/xcshareddata/xcschemes/swift-sdk.xcscheme index 5749aca4b..570b83965 100644 --- a/swift-sdk.xcodeproj/xcshareddata/xcschemes/swift-sdk.xcscheme +++ b/swift-sdk.xcodeproj/xcshareddata/xcschemes/swift-sdk.xcscheme @@ -210,6 +210,11 @@ BlueprintName = "offline-events-tests" ReferencedContainer = "container:swift-sdk.xcodeproj"> + + + + From 256c5e8f44a7cafc00e827040be324faa9f904b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Wed, 2 Aug 2023 10:08:16 -0700 Subject: [PATCH 36/57] Adding code climate fix --- swift-sdk/Internal/DependencyContainerProtocol.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift-sdk/Internal/DependencyContainerProtocol.swift b/swift-sdk/Internal/DependencyContainerProtocol.swift index 2a1e9b434..68a0488e3 100644 --- a/swift-sdk/Internal/DependencyContainerProtocol.swift +++ b/swift-sdk/Internal/DependencyContainerProtocol.swift @@ -70,11 +70,11 @@ extension DependencyContainerProtocol { networkSession: networkSession, deviceMetadata: deviceMetadata, dateProvider: dateProvider) - lazy var offlineProcessor:OfflineRequestProcessor? = nil + lazy var offlineProcessor: OfflineRequestProcessor? = nil lazy var healthMonitor: HealthMonitor? = nil - if(offlineMode) { + if offlineMode { if let persistenceContextProvider = createPersistenceContextProvider() { let healthMonitorDataProvider = createHealthMonitorDataProvider(persistenceContextProvider: persistenceContextProvider) From 51d5eafec8f5b35bef4fd4bc98a5910baa2cfbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Wed, 2 Aug 2023 14:52:10 -0700 Subject: [PATCH 37/57] [MOB - 6584] - Additional coredata checks --- swift-sdk/Internal/IterableCoreDataPersistence.swift | 8 +++++++- swift-sdk/Internal/IterableTaskScheduler.swift | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/swift-sdk/Internal/IterableCoreDataPersistence.swift b/swift-sdk/Internal/IterableCoreDataPersistence.swift index 1770fa36a..9ab99c0cc 100644 --- a/swift-sdk/Internal/IterableCoreDataPersistence.swift +++ b/swift-sdk/Internal/IterableCoreDataPersistence.swift @@ -160,7 +160,13 @@ struct CoreDataPersistenceContext: IterablePersistenceContext { func deleteAllTasks() throws { let taskManagedObjects: [IterableTaskManagedObject] = try CoreDataUtil.findAll(context: managedObjectContext, entity: PersistenceConst.Entity.Task.name) - taskManagedObjects.forEach { managedObjectContext.delete($0) } + taskManagedObjects.forEach { + if !$0.isDeleted { + managedObjectContext.delete($0) + } else { + ITBDebug("task already deleted") + } + } } func countTasks() throws -> Int { diff --git a/swift-sdk/Internal/IterableTaskScheduler.swift b/swift-sdk/Internal/IterableTaskScheduler.swift index 1ce67aee2..1300eedd6 100644 --- a/swift-sdk/Internal/IterableTaskScheduler.swift +++ b/swift-sdk/Internal/IterableTaskScheduler.swift @@ -55,8 +55,10 @@ class IterableTaskScheduler { try persistenceContext.deleteAllTasks() try persistenceContext.save() } catch let error { - ITBError("deleteAllTasks: \(error.localizedDescription)") - self?.healthMonitor.onDeleteAllTasksError() + DispatchQueue.main.async { + ITBError("deleteAllTasks: \(error.localizedDescription)") + self?.healthMonitor.onDeleteAllTasksError() + } } } } From a4d979812095ef2b71986b86dcc519238f4b65fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Wed, 2 Aug 2023 18:19:48 -0700 Subject: [PATCH 38/57] Reverting main thread handling --- swift-sdk/Internal/IterableTaskScheduler.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/swift-sdk/Internal/IterableTaskScheduler.swift b/swift-sdk/Internal/IterableTaskScheduler.swift index 1300eedd6..f35243548 100644 --- a/swift-sdk/Internal/IterableTaskScheduler.swift +++ b/swift-sdk/Internal/IterableTaskScheduler.swift @@ -54,11 +54,9 @@ class IterableTaskScheduler { do { try persistenceContext.deleteAllTasks() try persistenceContext.save() - } catch let error { - DispatchQueue.main.async { - ITBError("deleteAllTasks: \(error.localizedDescription)") - self?.healthMonitor.onDeleteAllTasksError() - } + } catch { + ITBError("deleteAllTasks: \(error.localizedDescription)") + self?.healthMonitor.onDeleteAllTasksError() } } } From 48a5e9ec1844bf0a4171fb19e61ce82f90468285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Thu, 3 Aug 2023 07:40:24 -0700 Subject: [PATCH 39/57] Allow persistence object creation Allow persistenceContextProvider to be created. To see if InAppFilePersistence tests run okay with it. --- .../DependencyContainerProtocol.swift | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/swift-sdk/Internal/DependencyContainerProtocol.swift b/swift-sdk/Internal/DependencyContainerProtocol.swift index 68a0488e3..ceba7198b 100644 --- a/swift-sdk/Internal/DependencyContainerProtocol.swift +++ b/swift-sdk/Internal/DependencyContainerProtocol.swift @@ -72,29 +72,32 @@ extension DependencyContainerProtocol { dateProvider: dateProvider) lazy var offlineProcessor: OfflineRequestProcessor? = nil lazy var healthMonitor: HealthMonitor? = nil - - + guard let persistenceContextProvider = createPersistenceContextProvider() else { + return RequestHandler(onlineProcessor: onlineProcessor, + offlineProcessor: nil, + healthMonitor: nil, + offlineMode: offlineMode) + } if offlineMode { - if let persistenceContextProvider = createPersistenceContextProvider() { - - let healthMonitorDataProvider = createHealthMonitorDataProvider(persistenceContextProvider: persistenceContextProvider) + + let healthMonitorDataProvider = createHealthMonitorDataProvider(persistenceContextProvider: persistenceContextProvider) healthMonitor = HealthMonitor(dataProvider: healthMonitorDataProvider, - dateProvider: dateProvider, - networkSession: networkSession) + dateProvider: dateProvider, + networkSession: networkSession) offlineProcessor = OfflineRequestProcessor(apiKey: apiKey, - authProvider: authProvider, - authManager: authManager, - endpoint: endpoint, - deviceMetadata: deviceMetadata, - taskScheduler: createTaskScheduler(persistenceContextProvider: persistenceContextProvider, - healthMonitor: healthMonitor!), - taskRunner: createTaskRunner(persistenceContextProvider: persistenceContextProvider, - healthMonitor: healthMonitor!), - notificationCenter: notificationCenter) - } - - return RequestHandler(onlineProcessor: onlineProcessor, + authProvider: authProvider, + authManager: authManager, + endpoint: endpoint, + deviceMetadata: deviceMetadata, + taskScheduler: createTaskScheduler(persistenceContextProvider: persistenceContextProvider, + healthMonitor: healthMonitor!), + taskRunner: createTaskRunner(persistenceContextProvider: persistenceContextProvider, + healthMonitor: healthMonitor!), + notificationCenter: notificationCenter) + + + return RequestHandler(onlineProcessor: onlineProcessor, offlineProcessor: offlineProcessor, healthMonitor: healthMonitor, offlineMode: offlineMode) From fd60fa9676e975423ad55439f8d964d2f03cdc9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Thu, 3 Aug 2023 09:27:55 -0700 Subject: [PATCH 40/57] [MOB - 6674] - Prepare for 6.4.15 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcab5547a..7df1a0215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ config.dataRegion = IterableDataRegion.EU; [IterableAPI initializeWithApiKey:@"" launchOptions:launchOptions config:config]; ``` +### Fixed +- Offline Mode is now off by default. Offline mode components will only load when the `offlineMode` configuration for RequestHandler is set to true. + +### Changed +- Offline mode configuration now persists throughout the current app session. Changes will take effect from the next app session. + ## 6.4.14 ### Added - Success and Failure handlers can now be passed to following functions: From 812cfd3538d1443e25d1c2c8f2967be389790b85 Mon Sep 17 00:00:00 2001 From: harrymash2006 Date: Wed, 1 Nov 2023 18:19:56 +0530 Subject: [PATCH 41/57] Add Retry mechanism for http requests --- swift-sdk/Internal/NetworkHelper.swift | 58 +++++++++++++------ .../unit-tests/IterableAPIResponseTests.swift | 24 ++++++++ 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/swift-sdk/Internal/NetworkHelper.swift b/swift-sdk/Internal/NetworkHelper.swift index 657bfc0e9..a253fa77b 100644 --- a/swift-sdk/Internal/NetworkHelper.swift +++ b/swift-sdk/Internal/NetworkHelper.swift @@ -28,6 +28,9 @@ extension NetworkError: LocalizedError { } struct NetworkHelper { + static let maxRetryCount = 5 + static let retryDelaySeconds = 2 + static func getData(fromUrl url: URL, usingSession networkSession: NetworkSessionProtocol) -> Pending { let fulfill = Fulfill() @@ -73,29 +76,46 @@ struct NetworkHelper { #endif let fulfill = Fulfill() - - networkSession.makeRequest(request) { data, response, error in - let result = createResultFromNetworkResponse(data: data, - converter: converter, - response: response, - error: error) - switch result { - case let .success(value): - #if NETWORK_DEBUG - print("request with id: \(requestId) successfully sent, response:") - print(value) - #endif - fulfill.resolve(with: value) - case let .failure(error): - #if NETWORK_DEBUG - print("request with id: \(requestId) errored") - print(error) - #endif - fulfill.reject(with: error) + func sendRequestWithRetries(request: URLRequest, retriesLeft: Int) { + networkSession.makeRequest(request) { data, response, error in + let result = createResultFromNetworkResponse(data: data, + converter: converter, + response: response, + error: error) + switch result { + case let .success(value): + #if NETWORK_DEBUG + print("request with id: \(requestId) successfully sent, response:") + print(value) + #endif + fulfill.resolve(with: value) + case let .failure(error): + if error.httpStatusCode ?? 0 >= 500 && retriesLeft > 0 { + #if NETWORK_DEBUG + print("retry attempt: \(maxRetryCount-retriesLeft+1) for url: \(request.url?.absoluteString ?? "")") + print(error) + #endif + var delay: DispatchTimeInterval = .seconds(0) + if (retriesLeft <= 3) { + delay = .seconds(retryDelaySeconds) + } + DispatchQueue.global().asyncAfter(deadline: .now() + delay) { + sendRequestWithRetries(request: request, retriesLeft: retriesLeft - 1) + } + } else { + #if NETWORK_DEBUG + print("request with id: \(requestId) errored") + print(error) + #endif + fulfill.reject(with: error) + } + } } } + sendRequestWithRetries(request: request, retriesLeft: maxRetryCount) + return fulfill } diff --git a/tests/unit-tests/IterableAPIResponseTests.swift b/tests/unit-tests/IterableAPIResponseTests.swift index 67881ac3e..071166ce6 100644 --- a/tests/unit-tests/IterableAPIResponseTests.swift +++ b/tests/unit-tests/IterableAPIResponseTests.swift @@ -152,6 +152,30 @@ class IterableAPIResponseTests: XCTestCase { wait(for: [xpectation], timeout: testExpectationTimeout) } + func testSendRequestWithRetry() { + let xpectation = expectation(description: "retry on status code >= 500") + + let networkSession = MockNetworkSession { _ in + MockNetworkSession.MockResponse(statusCode: 503, + data: Data(), + delay: 0) + } + + let iterableRequest = IterableRequest.post(PostRequest(path: "", args: nil, body: [:])) + + let apiClient = createApiClient(networkSession: networkSession) + var urlRequest = apiClient.convertToURLRequest(iterableRequest: iterableRequest)! + urlRequest.timeoutInterval = 1 + + RequestSender.sendRequest(urlRequest, usingSession: networkSession).onError { sendError in + xpectation.fulfill() + XCTAssert(sendError.reason!.lowercased().contains("internal server error")) + } + + wait(for: [xpectation], timeout: testExpectationTimeout) + } + + func testNetworkTimeoutResponse() { let xpectation = expectation(description: "timeout network response") From e03772a677de73b8588706a8fa76024cea935ae9 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 10:32:29 -0700 Subject: [PATCH 42/57] stashed changes --- swift-sdk/Internal/NetworkHelper.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/swift-sdk/Internal/NetworkHelper.swift b/swift-sdk/Internal/NetworkHelper.swift index a253fa77b..52bfe56c6 100644 --- a/swift-sdk/Internal/NetworkHelper.swift +++ b/swift-sdk/Internal/NetworkHelper.swift @@ -92,6 +92,7 @@ struct NetworkHelper { fulfill.resolve(with: value) case let .failure(error): if error.httpStatusCode ?? 0 >= 500 && retriesLeft > 0 { + print("url::sendRequestWithRetries: \(request.url)::\(retriesLeft)") #if NETWORK_DEBUG print("retry attempt: \(maxRetryCount-retriesLeft+1) for url: \(request.url?.absoluteString ?? "")") print(error) From 8ca1edd84ef2deb76ee397185b109f0f9ae2838e Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 11:08:42 -0700 Subject: [PATCH 43/57] adds jwt logic and unit tests --- swift-sdk.xcodeproj/project.pbxproj | 16 +++++++ swift-sdk/Internal/RequestProcessorUtil.swift | 2 +- tests/common/MockAuthManager.swift | 43 +++++++++++++++++ .../unit-tests/IterableAPIResponseTests.swift | 48 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 tests/common/MockAuthManager.swift diff --git a/swift-sdk.xcodeproj/project.pbxproj b/swift-sdk.xcodeproj/project.pbxproj index 9a28261ab..fe5c22132 100644 --- a/swift-sdk.xcodeproj/project.pbxproj +++ b/swift-sdk.xcodeproj/project.pbxproj @@ -172,6 +172,13 @@ 5B5AA717284F1A6D0093FED4 /* MockNetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5AA710284F1A6D0093FED4 /* MockNetworkSession.swift */; }; 5B6C3C1127CE871F00B9A753 /* NavInboxSessionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C3C1027CE871F00B9A753 /* NavInboxSessionUITests.swift */; }; 5B88BC482805D09D004016E5 /* NetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88BC472805D09D004016E5 /* NetworkSession.swift */; }; + 9FF05EAC2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; + 9FF05EAD2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; + 9FF05EAE2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; + 9FF05EAF2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; + 9FF05EB02AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; + 9FF05EB12AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; + 9FF05EB22AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; }; AC02480822791E2100495FB9 /* IterableInboxNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC02480722791E2100495FB9 /* IterableInboxNavigationViewController.swift */; }; AC02CAA6234E50B5006617E0 /* RegistrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC02CAA5234E50B5006617E0 /* RegistrationTests.swift */; }; AC03094B21E532470003A288 /* InAppPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC03094A21E532470003A288 /* InAppPersistence.swift */; }; @@ -568,6 +575,7 @@ 5B6C3C1027CE871F00B9A753 /* NavInboxSessionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavInboxSessionUITests.swift; sourceTree = ""; }; 5B88BC472805D09D004016E5 /* NetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSession.swift; sourceTree = ""; }; 5BFC7CED27FC9AF300E77479 /* inbox-ui-tests-app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "inbox-ui-tests-app.entitlements"; sourceTree = ""; }; + 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAuthManager.swift; sourceTree = ""; }; AC02480722791E2100495FB9 /* IterableInboxNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxNavigationViewController.swift; sourceTree = ""; }; AC02CAA5234E50B5006617E0 /* RegistrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationTests.swift; sourceTree = ""; }; AC03094A21E532470003A288 /* InAppPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPersistence.swift; sourceTree = ""; }; @@ -1360,6 +1368,7 @@ 5588DF7D28C04494000697D7 /* MockUrlDelegate.swift */, 5588DF8D28C044DE000697D7 /* MockUrlOpener.swift */, 5588DFD528C04683000697D7 /* MockWebView.swift */, + 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */, ); path = common; sourceTree = ""; @@ -2089,6 +2098,7 @@ ACA2A91A24AB266F001DFD17 /* Mocks.swift in Sources */, 5588DFAB28C045AE000697D7 /* MockInAppFetcher.swift in Sources */, AC29D05C24B5A7E000A9E019 /* CI.swift in Sources */, + 9FF05EB12AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2102,6 +2112,7 @@ AC2C668720D3435700D46CC9 /* ActionRunnerTests.swift in Sources */, 00CB31B621096129004ACDEC /* TestUtils.swift in Sources */, AC89661E2124FBCE0051A6CD /* AutoRegistrationTests.swift in Sources */, + 9FF05EAF2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, ACA8D1A921965B7D001B1332 /* InAppTests.swift in Sources */, 5588DFB928C045E3000697D7 /* MockInAppDelegate.swift in Sources */, 5588DFD128C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */, @@ -2198,6 +2209,7 @@ buildActionMask = 2147483647; files = ( 5588DFB828C045E3000697D7 /* MockInAppDelegate.swift in Sources */, + 9FF05EAE2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, 5588DF7028C0442D000697D7 /* MockDateProvider.swift in Sources */, 5588DFF028C046FF000697D7 /* MockMessageViewControllerEventTracker.swift in Sources */, ACB37AB124026C1E0093A8EA /* SampleInboxViewDelegateImplementations.swift in Sources */, @@ -2261,6 +2273,7 @@ ACC6A8502323910D003CC4BE /* UITestsHelper.swift in Sources */, 5588DFAA28C045AE000697D7 /* MockInAppFetcher.swift in Sources */, ACFF4287246569D300FDF10D /* CommonExtensions.swift in Sources */, + 9FF05EB02AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2287,6 +2300,7 @@ 5588DFCE28C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */, 5588DFDE28C046B7000697D7 /* MockLocalStorage.swift in Sources */, ACA8D1A52196309C001B1332 /* Common.swift in Sources */, + 9FF05EAC2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, 5588DFB628C045E3000697D7 /* MockInAppDelegate.swift in Sources */, 5588DF8628C044BE000697D7 /* MockCustomActionDelegate.swift in Sources */, AC995F992166EE490099A184 /* CommonMocks.swift in Sources */, @@ -2325,6 +2339,7 @@ 5588DF7C28C04463000697D7 /* MockNotificationResponse.swift in Sources */, 5588DFD428C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */, 5588DFE428C046B7000697D7 /* MockLocalStorage.swift in Sources */, + 9FF05EB22AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, ACC362C924D2CA8C002C67BA /* Common.swift in Sources */, ACC362C524D2C190002C67BA /* TaskProcessorTests.swift in Sources */, AC2AED4224EBC60C000EE5F3 /* TaskRunnerTests.swift in Sources */, @@ -2343,6 +2358,7 @@ 5588DFE728C046D7000697D7 /* MockInboxState.swift in Sources */, ACFF42A424656CCE00FDF10D /* ViewController.swift in Sources */, 5588DF7F28C04494000697D7 /* MockUrlDelegate.swift in Sources */, + 9FF05EAD2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */, 5588DF8728C044BE000697D7 /* MockCustomActionDelegate.swift in Sources */, 5588DFD728C04683000697D7 /* MockWebView.swift in Sources */, 5588DFEF28C046FF000697D7 /* MockMessageViewControllerEventTracker.swift in Sources */, diff --git a/swift-sdk/Internal/RequestProcessorUtil.swift b/swift-sdk/Internal/RequestProcessorUtil.swift index 797295eaa..da24fe6f1 100644 --- a/swift-sdk/Internal/RequestProcessorUtil.swift +++ b/swift-sdk/Internal/RequestProcessorUtil.swift @@ -18,7 +18,7 @@ struct RequestProcessorUtil { .onError { error in if error.httpStatusCode == 401, error.iterableCode == JsonValue.Code.invalidJwtPayload { ITBError("invalid JWT token, trying again: \(error.reason ?? "")") - authManager?.requestNewAuthToken(hasFailedPriorAuth: true) { _ in + authManager?.requestNewAuthToken(hasFailedPriorAuth: false) { _ in requestProvider().onSuccess { json in reportSuccess(result: result, value: json, successHandler: onSuccess, identifier: identifier) }.onError { error in diff --git a/tests/common/MockAuthManager.swift b/tests/common/MockAuthManager.swift new file mode 100644 index 000000000..b492c3df7 --- /dev/null +++ b/tests/common/MockAuthManager.swift @@ -0,0 +1,43 @@ +// +// MockAuthManager.swift +// swift-sdk +// +// Created by HARDIK MASHRU on 08/11/23. +// Copyright © 2023 Iterable. All rights reserved. +// +import Foundation +@testable import IterableSDK + +class MockAuthManager: IterableAuthManagerProtocol { + var shouldRetry = true + var retryWasRequested = false + + func getAuthToken() -> String? { + return "AuthToken" + } + + func resetFailedAuthCount() { + + } + + func requestNewAuthToken(hasFailedPriorAuth: Bool, onSuccess: ((String?) -> Void)?) { + if shouldRetry { + // Simulate the authManager obtaining a new token + retryWasRequested = true + shouldRetry = false + onSuccess?("newAuthToken") + } else { + // Simulate failing to obtain a new token + retryWasRequested = false + onSuccess?(nil) + } + } + + func setNewToken(_ newToken: String) { + + } + + func logoutUser() { + + } +} diff --git a/tests/unit-tests/IterableAPIResponseTests.swift b/tests/unit-tests/IterableAPIResponseTests.swift index 67881ac3e..804256634 100644 --- a/tests/unit-tests/IterableAPIResponseTests.swift +++ b/tests/unit-tests/IterableAPIResponseTests.swift @@ -100,6 +100,54 @@ class IterableAPIResponseTests: XCTestCase { wait(for: [xpectation], timeout: testExpectationTimeout) } + func testRetryOnInvalidJwtPayload() { + let xpectation = expectation(description: "retry on 401 with invalidJWTPayload") + + // Mock the dependencies and requestProvider for your test + let authManager = MockAuthManager() + + let networkErrorSession = MockNetworkSession() { _ in + MockNetworkSession.MockResponse(statusCode: 401, + data: ["code":"InvalidJwtPayload"].toJsonData(), + delay: 1) + } + + let networkSuccessSession = MockNetworkSession() { _ in + MockNetworkSession.MockResponse(statusCode: 200, + data: ["msg": "success"].toJsonData(), + delay: 1) + } + + let urlErrorRequest = createApiClient(networkSession: networkErrorSession).convertToURLRequest(iterableRequest: IterableRequest.post(PostRequest(path: "", args: nil, body: [:])))! + + + let urlSuccessRequest = createApiClient(networkSession: networkSuccessSession).convertToURLRequest(iterableRequest: IterableRequest.post(PostRequest(path: "", args: nil, body: [:])))! + + let requestProvider: () -> Pending = { + if authManager.retryWasRequested { + return RequestSender.sendRequest(urlSuccessRequest, usingSession: networkSuccessSession) + } + return RequestSender.sendRequest(urlErrorRequest, usingSession: networkErrorSession) + } + + let result = RequestProcessorUtil.sendRequest( + requestProvider: requestProvider, + authManager: authManager, + requestIdentifier: "TestIdentifier" + ) + + result.onSuccess { value in + xpectation.fulfill() + XCTAssert(true) + }.onError { error in + if authManager.retryWasRequested { + xpectation.fulfill() + } + } + + waitForExpectations(timeout: testExpectationTimeout) + } + func testResponseCode401() { // 401 = unauthorized let xpectation = expectation(description: "401") let iterableRequest = IterableRequest.post(PostRequest(path: "", args: nil, body: [:])) From 7c4b1429e38a2ce10e49b243ecf19682ca182cf4 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 11:32:56 -0700 Subject: [PATCH 44/57] minor fix --- swift-sdk/Internal/NetworkHelper.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/swift-sdk/Internal/NetworkHelper.swift b/swift-sdk/Internal/NetworkHelper.swift index 52bfe56c6..ecf22b78e 100644 --- a/swift-sdk/Internal/NetworkHelper.swift +++ b/swift-sdk/Internal/NetworkHelper.swift @@ -92,13 +92,12 @@ struct NetworkHelper { fulfill.resolve(with: value) case let .failure(error): if error.httpStatusCode ?? 0 >= 500 && retriesLeft > 0 { - print("url::sendRequestWithRetries: \(request.url)::\(retriesLeft)") #if NETWORK_DEBUG print("retry attempt: \(maxRetryCount-retriesLeft+1) for url: \(request.url?.absoluteString ?? "")") print(error) #endif var delay: DispatchTimeInterval = .seconds(0) - if (retriesLeft <= 3) { + if retriesLeft <= 3 { delay = .seconds(retryDelaySeconds) } DispatchQueue.global().asyncAfter(deadline: .now() + delay) { From d2055d073a940d653b1a357b91e0b29e35471bed Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 12:04:33 -0700 Subject: [PATCH 45/57] refactoring --- swift-sdk/Internal/NetworkHelper.swift | 67 +++++++++++++++++--------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/swift-sdk/Internal/NetworkHelper.swift b/swift-sdk/Internal/NetworkHelper.swift index ecf22b78e..51069866f 100644 --- a/swift-sdk/Internal/NetworkHelper.swift +++ b/swift-sdk/Internal/NetworkHelper.swift @@ -85,35 +85,54 @@ struct NetworkHelper { error: error) switch result { case let .success(value): - #if NETWORK_DEBUG - print("request with id: \(requestId) successfully sent, response:") - print(value) - #endif - fulfill.resolve(with: value) + handleSuccess(requestId: requestId, value: value) case let .failure(error): - if error.httpStatusCode ?? 0 >= 500 && retriesLeft > 0 { - #if NETWORK_DEBUG - print("retry attempt: \(maxRetryCount-retriesLeft+1) for url: \(request.url?.absoluteString ?? "")") - print(error) - #endif - var delay: DispatchTimeInterval = .seconds(0) - if retriesLeft <= 3 { - delay = .seconds(retryDelaySeconds) - } - DispatchQueue.global().asyncAfter(deadline: .now() + delay) { - sendRequestWithRetries(request: request, retriesLeft: retriesLeft - 1) - } - } else { - #if NETWORK_DEBUG - print("request with id: \(requestId) errored") - print(error) - #endif - fulfill.reject(with: error) - } + handleFailure(requestId: requestId, request: request, error: error, retriesLeft: retriesLeft) } } } + func handleSuccess(requestId: String, value: T) { + #if NETWORK_DEBUG + print("request with id: \(requestId) successfully sent, response:") + print(value) + #endif + fulfill.resolve(with: value) + } + + func handleFailure(requestId: String, request: URLRequest, error: NetworkError, retriesLeft: Int) { + if shouldRetry(error: error, retriesLeft: retriesLeft) { + retryRequest(requestId: requestId, request: request, error: error, retriesLeft: retriesLeft) + } else { + #if NETWORK_DEBUG + print("request with id: \(requestId) errored") + print(error) + #endif + fulfill.reject(with: error) + } + + } + + func shouldRetry(error: NetworkError, retriesLeft: Int) -> Bool { + return error.httpStatusCode ?? 0 >= 500 && retriesLeft > 0 + } + + func retryRequest(requestId: String, request: URLRequest, error: NetworkError, retriesLeft: Int) { + #if NETWORK_DEBUG + print("retry attempt: \(maxRetryCount-retriesLeft+1) for url: \(request.url?.absoluteString ?? "")") + print(error) + #endif + + var delay: DispatchTimeInterval = .seconds(0) + if retriesLeft <= 3 { + delay = .seconds(retryDelaySeconds) + } + + DispatchQueue.global().asyncAfter(deadline: .now() + delay) { + sendRequestWithRetries(request: request, retriesLeft: retriesLeft - 1) + } + } + sendRequestWithRetries(request: request, retriesLeft: maxRetryCount) return fulfill From 91b4b4c0f0b0b4208209db8286b8ede102b67c06 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 12:32:07 -0700 Subject: [PATCH 46/57] passes in request id --- swift-sdk/Internal/NetworkHelper.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swift-sdk/Internal/NetworkHelper.swift b/swift-sdk/Internal/NetworkHelper.swift index 51069866f..8edb8cca4 100644 --- a/swift-sdk/Internal/NetworkHelper.swift +++ b/swift-sdk/Internal/NetworkHelper.swift @@ -77,7 +77,7 @@ struct NetworkHelper { let fulfill = Fulfill() - func sendRequestWithRetries(request: URLRequest, retriesLeft: Int) { + func sendRequestWithRetries(request: URLRequest, requestId: String, retriesLeft: Int) { networkSession.makeRequest(request) { data, response, error in let result = createResultFromNetworkResponse(data: data, converter: converter, @@ -129,11 +129,11 @@ struct NetworkHelper { } DispatchQueue.global().asyncAfter(deadline: .now() + delay) { - sendRequestWithRetries(request: request, retriesLeft: retriesLeft - 1) + sendRequestWithRetries(request: request, requestId: requestId, retriesLeft: retriesLeft - 1) } } - sendRequestWithRetries(request: request, retriesLeft: maxRetryCount) + sendRequestWithRetries(request: request, requestId: requestId, retriesLeft: maxRetryCount) return fulfill } From e6566b5a584a924b5d279f14700aa7f17bc6e6bf Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 12:44:27 -0700 Subject: [PATCH 47/57] minor edit --- swift-sdk/Internal/NetworkHelper.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swift-sdk/Internal/NetworkHelper.swift b/swift-sdk/Internal/NetworkHelper.swift index 8edb8cca4..af3234330 100644 --- a/swift-sdk/Internal/NetworkHelper.swift +++ b/swift-sdk/Internal/NetworkHelper.swift @@ -55,8 +55,9 @@ struct NetworkHelper { static func sendRequest(_ request: URLRequest, converter: @escaping (Data) throws -> T?, usingSession networkSession: NetworkSessionProtocol) -> Pending { - #if NETWORK_DEBUG + let requestId = IterableUtil.generateUUID() + #if NETWORK_DEBUG print() print("====================================================>") print("sending request: \(request)") From 88032d980615301d2e768c0f62cefc366d2c81ce Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 10 Nov 2023 14:28:15 -0700 Subject: [PATCH 48/57] skips old unit test --- tests/unit-tests/AuthTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit-tests/AuthTests.swift b/tests/unit-tests/AuthTests.swift index eac7136ca..736dd0ede 100644 --- a/tests/unit-tests/AuthTests.swift +++ b/tests/unit-tests/AuthTests.swift @@ -525,7 +525,9 @@ class AuthTests: XCTestCase { XCTAssertNil(internalAPI.auth.authToken) } - func testAuthTokenRefreshRetryOnlyOnce() { + func testAuthTokenRefreshRetryOnlyOnce() throws { + throw XCTSkip("skipping this test") + let condition1 = expectation(description: "\(#function) - callback not called correctly in some form") condition1.expectedFulfillmentCount = 2 From 30911911b83588164e01f6c0a2238570083c4175 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 08:43:32 -0700 Subject: [PATCH 49/57] adds reason for skipping test --- tests/unit-tests/AuthTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-tests/AuthTests.swift b/tests/unit-tests/AuthTests.swift index 736dd0ede..52391d779 100644 --- a/tests/unit-tests/AuthTests.swift +++ b/tests/unit-tests/AuthTests.swift @@ -526,7 +526,7 @@ class AuthTests: XCTestCase { } func testAuthTokenRefreshRetryOnlyOnce() throws { - throw XCTSkip("skipping this test") + throw XCTSkip("skipping this test - auth token retries should occur more than once") let condition1 = expectation(description: "\(#function) - callback not called correctly in some form") condition1.expectedFulfillmentCount = 2 From 8087c4f51dcdd1b8117e21f6a4bc53cb92abe01f Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 11:39:32 -0700 Subject: [PATCH 50/57] prepares for release --- CHANGELOG.md | 7 +++++++ Iterable-iOS-AppExtensions.podspec | 2 +- Iterable-iOS-SDK.podspec | 2 +- swift-sdk/IterableAPI.swift | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7df1a0215..e0602d0c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.4.16 +### Added +- `sendRequewstWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response + +### Changed +- updates `sendRequest` in `RequestProcessorUtil` to request a new token regardless of if the prior authentication attempt failed + ## 6.4.15 ### Added - This release allows you to use projects hosted on Iterable's EU data center. If your project is hosted on Iterable's [European data center (EUDC)](https://support.iterable.com/hc/articles/17572750887444), configure the SDK to use Iterable's EU-based API endpoints: diff --git a/Iterable-iOS-AppExtensions.podspec b/Iterable-iOS-AppExtensions.podspec index c1f0c8d96..2fe946d62 100644 --- a/Iterable-iOS-AppExtensions.podspec +++ b/Iterable-iOS-AppExtensions.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-AppExtensions" s.module_name = "IterableAppExtensions" - s.version = "6.4.15" + s.version = "6.4.16" s.summary = "App Extensions for Iterable SDK" s.description = <<-DESC diff --git a/Iterable-iOS-SDK.podspec b/Iterable-iOS-SDK.podspec index 57d3fbab6..adffcf24e 100644 --- a/Iterable-iOS-SDK.podspec +++ b/Iterable-iOS-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-SDK" s.module_name = "IterableSDK" - s.version = "6.4.15" + s.version = "6.4.16" s.summary = "Iterable's official SDK for iOS" s.description = <<-DESC diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 9c57fd4b3..1eb2b952a 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -7,7 +7,7 @@ import UIKit @objcMembers public final class IterableAPI: NSObject { /// The current SDK version - public static let sdkVersion = "6.4.15" + public static let sdkVersion = "6.4.16" /// The email of the logged in user that this IterableAPI is using public static var email: String? { From 36c7b09865b377f91a212f65471b68dc55abf842 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 13:26:32 -0700 Subject: [PATCH 51/57] updates changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0602d0c1..3d458c1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `sendRequewstWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response ### Changed -- updates `sendRequest` in `RequestProcessorUtil` to request a new token regardless of if the prior authentication attempt failed +- updates `sendRequest` in `RequestProcessorUtil` to retries API request that resulted in a 401 response upon receipt of a new JWT ## 6.4.15 ### Added From 9c153b58eb71a0cccbc9207dcaf45173739d00f6 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 13:34:20 -0700 Subject: [PATCH 52/57] updates changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d458c1b0..8d5f7cf16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `sendRequewstWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response ### Changed -- updates `sendRequest` in `RequestProcessorUtil` to retries API request that resulted in a 401 response upon receipt of a new JWT +- updates `sendRequest` in `RequestProcessorUtil` to retry the API request that resulted in a 401 response upon receipt of a new JWT ## 6.4.15 ### Added From 62931bed4e93cd4bd08a3eeb9264a738d5594be8 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 13:35:40 -0700 Subject: [PATCH 53/57] updates version numbers --- Iterable-iOS-AppExtensions.podspec | 2 +- Iterable-iOS-SDK.podspec | 2 +- swift-sdk/IterableAPI.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Iterable-iOS-AppExtensions.podspec b/Iterable-iOS-AppExtensions.podspec index 2fe946d62..74ff5abb6 100644 --- a/Iterable-iOS-AppExtensions.podspec +++ b/Iterable-iOS-AppExtensions.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-AppExtensions" s.module_name = "IterableAppExtensions" - s.version = "6.4.16" + s.version = "6.5.0" s.summary = "App Extensions for Iterable SDK" s.description = <<-DESC diff --git a/Iterable-iOS-SDK.podspec b/Iterable-iOS-SDK.podspec index adffcf24e..334e07e35 100644 --- a/Iterable-iOS-SDK.podspec +++ b/Iterable-iOS-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-SDK" s.module_name = "IterableSDK" - s.version = "6.4.16" + s.version = "6.5.0" s.summary = "Iterable's official SDK for iOS" s.description = <<-DESC diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 1eb2b952a..6d2e1dd79 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -7,7 +7,7 @@ import UIKit @objcMembers public final class IterableAPI: NSObject { /// The current SDK version - public static let sdkVersion = "6.4.16" + public static let sdkVersion = "6.5.0" /// The email of the logged in user that this IterableAPI is using public static var email: String? { From e3393ba4019c2536bc27bc2277dbb46ce6485eb2 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 13:49:35 -0700 Subject: [PATCH 54/57] updates version number in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d5f7cf16..e492bcf4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## 6.4.16 +## 6.5.0 ### Added - `sendRequewstWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response From bb487c32a0420ae86f013ac6f3108f7133c16b12 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 14:03:24 -0700 Subject: [PATCH 55/57] updates changelog and version numbers --- CHANGELOG.md | 4 ++-- Iterable-iOS-AppExtensions.podspec | 2 +- Iterable-iOS-SDK.podspec | 2 +- swift-sdk/IterableAPI.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e492bcf4c..7ff4fc635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## 6.5.0 +## 6.4.16 ### Added -- `sendRequewstWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response +- `sendRequestWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response ### Changed - updates `sendRequest` in `RequestProcessorUtil` to retry the API request that resulted in a 401 response upon receipt of a new JWT diff --git a/Iterable-iOS-AppExtensions.podspec b/Iterable-iOS-AppExtensions.podspec index 74ff5abb6..2fe946d62 100644 --- a/Iterable-iOS-AppExtensions.podspec +++ b/Iterable-iOS-AppExtensions.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-AppExtensions" s.module_name = "IterableAppExtensions" - s.version = "6.5.0" + s.version = "6.4.16" s.summary = "App Extensions for Iterable SDK" s.description = <<-DESC diff --git a/Iterable-iOS-SDK.podspec b/Iterable-iOS-SDK.podspec index 334e07e35..adffcf24e 100644 --- a/Iterable-iOS-SDK.podspec +++ b/Iterable-iOS-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Iterable-iOS-SDK" s.module_name = "IterableSDK" - s.version = "6.5.0" + s.version = "6.4.16" s.summary = "Iterable's official SDK for iOS" s.description = <<-DESC diff --git a/swift-sdk/IterableAPI.swift b/swift-sdk/IterableAPI.swift index 6d2e1dd79..1eb2b952a 100644 --- a/swift-sdk/IterableAPI.swift +++ b/swift-sdk/IterableAPI.swift @@ -7,7 +7,7 @@ import UIKit @objcMembers public final class IterableAPI: NSObject { /// The current SDK version - public static let sdkVersion = "6.5.0" + public static let sdkVersion = "6.4.16" /// The email of the logged in user that this IterableAPI is using public static var email: String? { From 95e263aaa67d455074510de1f5f54d01d7f2e628 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 14:22:36 -0700 Subject: [PATCH 56/57] updates changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff4fc635..30a86207f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 6.4.16 ### Added -- `sendRequestWithRetries` function added as part of the `NetworkHelperclass` which allows for multiple retries to be triggered when a 500 and above status code is returned in the response +- `sendRequestWithRetries` function added as part of the `NetworkHelperclass` which automatically retries network request when a 500 and above status code is returned in the response ### Changed - updates `sendRequest` in `RequestProcessorUtil` to retry the API request that resulted in a 401 response upon receipt of a new JWT +- updates `NetworkHelper` class logic to use `sendRequestWithRetries`method which wraps the original `networkSession.makeRequest` ## 6.4.15 ### Added From 34b35df374709074c1794a75fc30b73454a7b4ae Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Mon, 13 Nov 2023 14:34:08 -0700 Subject: [PATCH 57/57] updates changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30a86207f..2c7849733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,13 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 6.4.16 ### Added -- `sendRequestWithRetries` function added as part of the `NetworkHelperclass` which automatically retries network request when a 500 and above status code is returned in the response +- `sendRequestWithRetries` function added as part of the `NetworkHelperclass` +- For an API request that results in a 500-level error response, the SDK now executes up to five retries. Before each of the final three attempts, there is a two-second delay. ### Changed - updates `sendRequest` in `RequestProcessorUtil` to retry the API request that resulted in a 401 response upon receipt of a new JWT - updates `NetworkHelper` class logic to use `sendRequestWithRetries`method which wraps the original `networkSession.makeRequest` +- When an API request fails with a 401 because of an invalid JWT token, the SDK now immediately requests a new JWT token for the signed-in user. ## 6.4.15 ### Added