From 596eed4d18b52644433fb6ab112a5734ace05524 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Wed, 12 Feb 2025 14:53:12 -0700 Subject: [PATCH 1/3] adds placement ids array to sync messages --- .../Protocols/IterableEmbeddedManagerProtocol.swift | 1 + swift-sdk/Internal/IterableEmbeddedManager.swift | 11 ++++++++--- swift-sdk/Internal/api-client/ApiClient.swift | 6 +++++- swift-sdk/Internal/api-client/ApiClientProtocol.swift | 1 + .../Internal/api-client/Request/RequestCreator.swift | 8 ++++++++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift b/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift index a38c652f3..ee6a195de 100644 --- a/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift +++ b/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift @@ -11,6 +11,7 @@ public protocol IterableEmbeddedManagerProtocol { func removeUpdateListener(_ listener: IterableEmbeddedUpdateDelegate) func syncMessages(completion: @escaping () -> Void) + func syncMessages(placementIds: [Int], completion: @escaping () -> Void) func handleEmbeddedClick(message: IterableEmbeddedMessage, buttonIdentifier: String?, clickedUrl: String) func reset() } diff --git a/swift-sdk/Internal/IterableEmbeddedManager.swift b/swift-sdk/Internal/IterableEmbeddedManager.swift index 8ecd74b36..48e143b79 100644 --- a/swift-sdk/Internal/IterableEmbeddedManager.swift +++ b/swift-sdk/Internal/IterableEmbeddedManager.swift @@ -152,8 +152,8 @@ class IterableEmbeddedManager: NSObject, IterableInternalEmbeddedManagerProtocol syncMessages { } } - private func retrieveEmbeddedMessages(completion: @escaping () -> Void) { - apiClient.getEmbeddedMessages() + private func retrieveEmbeddedMessages(placementIds: [Int]?, completion: @escaping () -> Void) { + apiClient.getEmbeddedMessages(placementIds: placementIds) .onCompletion( receiveValue: { embeddedMessagesPayload in let placements = embeddedMessagesPayload.placements @@ -227,8 +227,13 @@ class IterableEmbeddedManager: NSObject, IterableInternalEmbeddedManagerProtocol extension IterableEmbeddedManager: EmbeddedNotifiable { public func syncMessages(completion: @escaping () -> Void) { + syncMessages(placementsIds: nil, completion: completion) + + } + + public func syncMessages(placementIds: [Int]?, completion: @escaping () -> Void) { if (enableEmbeddedMessaging) { - retrieveEmbeddedMessages(completion: completion) + retrieveEmbeddedMessages(placementIds: placementIds, completion: completion) } } } diff --git a/swift-sdk/Internal/api-client/ApiClient.swift b/swift-sdk/Internal/api-client/ApiClient.swift index 26aebf1d9..247b26acc 100644 --- a/swift-sdk/Internal/api-client/ApiClient.swift +++ b/swift-sdk/Internal/api-client/ApiClient.swift @@ -220,7 +220,11 @@ extension ApiClient: ApiClientProtocol { // MARK: - Embedded Messaging func getEmbeddedMessages() -> Pending { - let result = createRequestCreator().flatMap { $0.createGetEmbeddedMessagesRequest() } + return getEmbeddedMessages(placementIds: []) + } + + func getEmbeddedMessages(placementIds: [Int]) -> Pending { + let result = createRequestCreator().flatMap { $0.createGetEmbeddedMessagesRequest(placementIds: placementIds) } return send(iterableRequestResult: result) } diff --git a/swift-sdk/Internal/api-client/ApiClientProtocol.swift b/swift-sdk/Internal/api-client/ApiClientProtocol.swift index ce3f377a6..9e5bb709c 100644 --- a/swift-sdk/Internal/api-client/ApiClientProtocol.swift +++ b/swift-sdk/Internal/api-client/ApiClientProtocol.swift @@ -47,6 +47,7 @@ protocol ApiClientProtocol: AnyObject { func getRemoteConfiguration() -> Pending func getEmbeddedMessages() -> Pending + func getEmbeddedMessages(placementIds: [Int]) -> Pending @discardableResult func track(embeddedMessageReceived message: IterableEmbeddedMessage) -> Pending diff --git a/swift-sdk/Internal/api-client/Request/RequestCreator.swift b/swift-sdk/Internal/api-client/Request/RequestCreator.swift index 9a14fba24..06ce1746c 100644 --- a/swift-sdk/Internal/api-client/Request/RequestCreator.swift +++ b/swift-sdk/Internal/api-client/Request/RequestCreator.swift @@ -424,6 +424,10 @@ struct RequestCreator { // MARK: - Embedded Messaging Request Calls func createGetEmbeddedMessagesRequest() -> Result { + return createGetEmbeddedMessagesRequest(placementIds: []) + } + + func createGetEmbeddedMessagesRequest(placementIds: [Int]) -> Result { if case .none = auth.emailOrUserId { ITBError(Self.authMissingMessage) return .failure(IterableError.general(description: Self.authMissingMessage)) @@ -437,6 +441,10 @@ struct RequestCreator { args[JsonKey.Embedded.packageName] = packageName } + if !placementIds.isEmpty { + args["placementIds"] = placementIds + } + setCurrentUser(inDict: &args) return .success(.get(createGetRequest(forPath: Const.Path.getEmbeddedMessages, withArgs: args as! [String: String]))) From 152b30a984ec9d90713d5b9511b5b2b05db702a1 Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Wed, 12 Feb 2025 15:56:46 -0700 Subject: [PATCH 2/3] adds unit test --- .../IterableEmbeddedManagerProtocol.swift | 2 +- swift-sdk/Internal/EmptyEmbeddedManager.swift | 4 ++ .../Internal/IterableEmbeddedManager.swift | 4 +- swift-sdk/Internal/api-client/ApiClient.swift | 4 +- .../api-client/ApiClientProtocol.swift | 2 +- tests/unit-tests/BlankApiClient.swift | 4 ++ tests/unit-tests/EmbeddedManagerTests.swift | 52 +++++++++++++++++-- 7 files changed, 63 insertions(+), 9 deletions(-) diff --git a/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift b/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift index ee6a195de..8be6d00d6 100644 --- a/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift +++ b/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift @@ -11,7 +11,7 @@ public protocol IterableEmbeddedManagerProtocol { func removeUpdateListener(_ listener: IterableEmbeddedUpdateDelegate) func syncMessages(completion: @escaping () -> Void) - func syncMessages(placementIds: [Int], completion: @escaping () -> Void) + func syncMessages(placementIds: [Int]?, completion: @escaping () -> Void) func handleEmbeddedClick(message: IterableEmbeddedMessage, buttonIdentifier: String?, clickedUrl: String) func reset() } diff --git a/swift-sdk/Internal/EmptyEmbeddedManager.swift b/swift-sdk/Internal/EmptyEmbeddedManager.swift index 7ea5d059b..25e581b00 100644 --- a/swift-sdk/Internal/EmptyEmbeddedManager.swift +++ b/swift-sdk/Internal/EmptyEmbeddedManager.swift @@ -25,6 +25,10 @@ class EmptyEmbeddedManager: IterableInternalEmbeddedManagerProtocol { } + func syncMessages(placementIds: [Int]?, completion: @escaping () -> Void) { + + } + public func handleEmbeddedClick(message: IterableEmbeddedMessage, buttonIdentifier: String?, clickedUrl: String) { } diff --git a/swift-sdk/Internal/IterableEmbeddedManager.swift b/swift-sdk/Internal/IterableEmbeddedManager.swift index 48e143b79..98938f991 100644 --- a/swift-sdk/Internal/IterableEmbeddedManager.swift +++ b/swift-sdk/Internal/IterableEmbeddedManager.swift @@ -153,7 +153,7 @@ class IterableEmbeddedManager: NSObject, IterableInternalEmbeddedManagerProtocol } private func retrieveEmbeddedMessages(placementIds: [Int]?, completion: @escaping () -> Void) { - apiClient.getEmbeddedMessages(placementIds: placementIds) + apiClient.getEmbeddedMessages(placementIds: placementIds ?? []) .onCompletion( receiveValue: { embeddedMessagesPayload in let placements = embeddedMessagesPayload.placements @@ -227,7 +227,7 @@ class IterableEmbeddedManager: NSObject, IterableInternalEmbeddedManagerProtocol extension IterableEmbeddedManager: EmbeddedNotifiable { public func syncMessages(completion: @escaping () -> Void) { - syncMessages(placementsIds: nil, completion: completion) + syncMessages(placementIds: nil, completion: completion) } diff --git a/swift-sdk/Internal/api-client/ApiClient.swift b/swift-sdk/Internal/api-client/ApiClient.swift index 247b26acc..21738c1dc 100644 --- a/swift-sdk/Internal/api-client/ApiClient.swift +++ b/swift-sdk/Internal/api-client/ApiClient.swift @@ -223,8 +223,8 @@ extension ApiClient: ApiClientProtocol { return getEmbeddedMessages(placementIds: []) } - func getEmbeddedMessages(placementIds: [Int]) -> Pending { - let result = createRequestCreator().flatMap { $0.createGetEmbeddedMessagesRequest(placementIds: placementIds) } + func getEmbeddedMessages(placementIds: [Int]?) -> Pending { + let result = createRequestCreator().flatMap { $0.createGetEmbeddedMessagesRequest(placementIds: placementIds ?? []) } return send(iterableRequestResult: result) } diff --git a/swift-sdk/Internal/api-client/ApiClientProtocol.swift b/swift-sdk/Internal/api-client/ApiClientProtocol.swift index 9e5bb709c..e9caabba3 100644 --- a/swift-sdk/Internal/api-client/ApiClientProtocol.swift +++ b/swift-sdk/Internal/api-client/ApiClientProtocol.swift @@ -47,7 +47,7 @@ protocol ApiClientProtocol: AnyObject { func getRemoteConfiguration() -> Pending func getEmbeddedMessages() -> Pending - func getEmbeddedMessages(placementIds: [Int]) -> Pending + func getEmbeddedMessages(placementIds: [Int]?) -> Pending @discardableResult func track(embeddedMessageReceived message: IterableEmbeddedMessage) -> Pending diff --git a/tests/unit-tests/BlankApiClient.swift b/tests/unit-tests/BlankApiClient.swift index 5ce30d227..40491fb0c 100644 --- a/tests/unit-tests/BlankApiClient.swift +++ b/tests/unit-tests/BlankApiClient.swift @@ -83,6 +83,10 @@ class BlankApiClient: ApiClientProtocol { Pending() } + func getEmbeddedMessages(placementIds: [Int]?) -> Pending { + return Pending() + } + func track(embeddedMessageReceived message: IterableEmbeddedMessage) -> Pending { Pending() } diff --git a/tests/unit-tests/EmbeddedManagerTests.swift b/tests/unit-tests/EmbeddedManagerTests.swift index ec6bb5f13..7bd24b8ff 100644 --- a/tests/unit-tests/EmbeddedManagerTests.swift +++ b/tests/unit-tests/EmbeddedManagerTests.swift @@ -71,7 +71,7 @@ final class EmbeddedManagerTests: XCTestCase { XCTAssertEqual(message.metadata.placementId, 2, "Fetched message should have placementId 2") } } - + // syncMessages func testSyncMessagesSuccessful() { let syncMessagesExpectation = expectation(description: "syncMessages should complete") @@ -107,6 +107,40 @@ final class EmbeddedManagerTests: XCTestCase { wait(for: [syncMessagesExpectation, delegateExpectation], timeout: 2) } + func testSyncMessagesForSpecifiedPlacement() { + let mockApiClient = MockApiClient() + mockApiClient.populateMessages([ + 1: [IterableEmbeddedMessage(messageId: "1", placementId: 1)], + 2: [IterableEmbeddedMessage(messageId: "2", placementId: 2), + IterableEmbeddedMessage(messageId: "3", placementId: 2)], + 3: [IterableEmbeddedMessage(messageId: "4", placementId: 3)], + ]) + let manager = IterableEmbeddedManager(apiClient: mockApiClient, + urlDelegate: nil, + customActionDelegate: nil, + urlOpener: MockUrlOpener(), + allowedProtocols: [], + enableEmbeddedMessaging: true) + + // Sync only placement 2 + manager.syncMessages(placementIds: [2]) { } + + // Verify we got updated messages for placement 2 + let messagesForPlacement2 = manager.getMessages(for: 2) + XCTAssertEqual(messagesForPlacement2.count, 2, "Should have 2 messages for placementId 2") + for message in messagesForPlacement2 { + XCTAssertEqual(message.metadata.placementId, 2, "Fetched message should have placementId 2") + } + + // Verify other placements are not synced + let messagesForPlacement1 = manager.getMessages(for: 1) + XCTAssertEqual(messagesForPlacement1.count, 0, "Should have no messages for placementId 1") + + let messagesForPlacement3 = manager.getMessages(for: 3) + XCTAssertEqual(messagesForPlacement3.count, 0, "Should have no messages for placementId 3") + } + + func testManagerReset() { let syncMessagesExpectation = expectation(description: "syncMessages should complete") @@ -354,7 +388,8 @@ final class EmbeddedManagerTests: XCTestCase { private var newMessages = false private var invalidApiKey = false private var mockMessages: [Int: [IterableEmbeddedMessage]] = [:] - + private var lastRequestedPlacementIds: [Int]? + func haveNewEmbeddedMessages() { newMessages = true } @@ -369,13 +404,24 @@ final class EmbeddedManagerTests: XCTestCase { } override func getEmbeddedMessages() -> IterableSDK.Pending { + return getEmbeddedMessages(placementIds: nil) + } + + override func getEmbeddedMessages(placementIds: [Int]?) -> IterableSDK.Pending { + lastRequestedPlacementIds = placementIds + if invalidApiKey { return FailPending(error: IterableSDK.SendRequestError(reason: "Invalid API Key")) } if newMessages { + var filteredMessages = mockMessages + if let placementIds = placementIds, !placementIds.isEmpty { + filteredMessages = mockMessages.filter { placementIds.contains($0.key) } + } + var placements: [Placement] = [] - for (placementId, messages) in mockMessages { + for (placementId, messages) in filteredMessages { let placement = Placement(placementId: placementId, embeddedMessages: messages) placements.append(placement) } From 1f7fabd46dbb8d4b90445f12bb1a94a629a10e70 Mon Sep 17 00:00:00 2001 From: Joao Dordio Date: Wed, 2 Jul 2025 16:17:23 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Small=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- swift-sdk/Core/Constants.swift | 7 +++++++ .../Internal/IterableEmbeddedManager.swift | 2 +- swift-sdk/Internal/api-client/ApiClient.swift | 4 ++-- .../Internal/api-client/ApiClientProtocol.swift | 1 + .../api-client/Request/RequestCreator.swift | 17 +++++++---------- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/swift-sdk/Core/Constants.swift b/swift-sdk/Core/Constants.swift index 7b2797ba2..6688157b4 100644 --- a/swift-sdk/Core/Constants.swift +++ b/swift-sdk/Core/Constants.swift @@ -189,6 +189,7 @@ enum JsonKey { // embedded static let embeddedSessionId = "session" static let placementId = "placementId" + static let placementIds = "placementIds" static let embeddedSessionStart = "embeddedSessionStart" static let embeddedSessionEnd = "embeddedSessionEnd" static let embeddedButtonId = "buttonIdentifier" @@ -231,6 +232,12 @@ enum JsonKey { enum Embedded { static let packageName = "packageName" static let sdkVersion = "SDKVersion" + + enum Session { + static let id = "id" + static let start = "start" + static let end = "end" + } } enum Header { diff --git a/swift-sdk/Internal/IterableEmbeddedManager.swift b/swift-sdk/Internal/IterableEmbeddedManager.swift index cf0944e90..82bf8cffb 100644 --- a/swift-sdk/Internal/IterableEmbeddedManager.swift +++ b/swift-sdk/Internal/IterableEmbeddedManager.swift @@ -162,7 +162,7 @@ class IterableEmbeddedManager: NSObject, IterableInternalEmbeddedManagerProtocol } private func retrieveEmbeddedMessages(placementIds: [Int]?, completion: @escaping () -> Void) { - apiClient.getEmbeddedMessages(placementIds: placementIds ?? []) + apiClient.getEmbeddedMessages(placementIds: placementIds) .onCompletion( receiveValue: { embeddedMessagesPayload in let placements = embeddedMessagesPayload.placements diff --git a/swift-sdk/Internal/api-client/ApiClient.swift b/swift-sdk/Internal/api-client/ApiClient.swift index 21738c1dc..033d9a707 100644 --- a/swift-sdk/Internal/api-client/ApiClient.swift +++ b/swift-sdk/Internal/api-client/ApiClient.swift @@ -220,11 +220,11 @@ extension ApiClient: ApiClientProtocol { // MARK: - Embedded Messaging func getEmbeddedMessages() -> Pending { - return getEmbeddedMessages(placementIds: []) + return getEmbeddedMessages(placementIds: nil) } func getEmbeddedMessages(placementIds: [Int]?) -> Pending { - let result = createRequestCreator().flatMap { $0.createGetEmbeddedMessagesRequest(placementIds: placementIds ?? []) } + let result = createRequestCreator().flatMap { $0.createGetEmbeddedMessagesRequest(placementIds: placementIds) } return send(iterableRequestResult: result) } diff --git a/swift-sdk/Internal/api-client/ApiClientProtocol.swift b/swift-sdk/Internal/api-client/ApiClientProtocol.swift index e9caabba3..3a4408051 100644 --- a/swift-sdk/Internal/api-client/ApiClientProtocol.swift +++ b/swift-sdk/Internal/api-client/ApiClientProtocol.swift @@ -47,6 +47,7 @@ protocol ApiClientProtocol: AnyObject { func getRemoteConfiguration() -> Pending func getEmbeddedMessages() -> Pending + func getEmbeddedMessages(placementIds: [Int]?) -> Pending @discardableResult func track(embeddedMessageReceived message: IterableEmbeddedMessage) -> Pending diff --git a/swift-sdk/Internal/api-client/Request/RequestCreator.swift b/swift-sdk/Internal/api-client/Request/RequestCreator.swift index 06ce1746c..ca1b711ef 100644 --- a/swift-sdk/Internal/api-client/Request/RequestCreator.swift +++ b/swift-sdk/Internal/api-client/Request/RequestCreator.swift @@ -423,11 +423,7 @@ struct RequestCreator { // MARK: - Embedded Messaging Request Calls - func createGetEmbeddedMessagesRequest() -> Result { - return createGetEmbeddedMessagesRequest(placementIds: []) - } - - func createGetEmbeddedMessagesRequest(placementIds: [Int]) -> Result { + func createGetEmbeddedMessagesRequest(placementIds: [Int]? = []) -> Result { if case .none = auth.emailOrUserId { ITBError(Self.authMissingMessage) return .failure(IterableError.general(description: Self.authMissingMessage)) @@ -441,8 +437,9 @@ struct RequestCreator { args[JsonKey.Embedded.packageName] = packageName } - if !placementIds.isEmpty { - args["placementIds"] = placementIds + if let placementIds = placementIds, + !placementIds.isEmpty { + args[JsonKey.placementIds] = placementIds } setCurrentUser(inDict: &args) @@ -550,9 +547,9 @@ struct RequestCreator { setCurrentUser(inDict: &body) body.setValue(for: JsonKey.embeddedSessionId, value: [ - "id": embeddedSessionId, - "start": IterableUtil.int(fromDate: sessionStartTime), - "end": IterableUtil.int(fromDate: sessionEndTime) + JsonKey.Embedded.Session.id: embeddedSessionId, + JsonKey.Embedded.Session.start: IterableUtil.int(fromDate: sessionStartTime), + JsonKey.Embedded.Session.end: IterableUtil.int(fromDate: sessionEndTime) ]) body.setValue(for: JsonKey.impressions, value: embeddedSession.impressions.compactMap { $0.asDictionary() })