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/Core/Protocols/IterableEmbeddedManagerProtocol.swift b/swift-sdk/Core/Protocols/IterableEmbeddedManagerProtocol.swift index a38c652f3..8be6d00d6 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/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 db4297a82..82bf8cffb 100644 --- a/swift-sdk/Internal/IterableEmbeddedManager.swift +++ b/swift-sdk/Internal/IterableEmbeddedManager.swift @@ -161,8 +161,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 @@ -245,8 +245,13 @@ class IterableEmbeddedManager: NSObject, IterableInternalEmbeddedManagerProtocol extension IterableEmbeddedManager: EmbeddedNotifiable { public func syncMessages(completion: @escaping () -> Void) { + syncMessages(placementIds: 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..033d9a707 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: nil) + } + + 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..3a4408051 100644 --- a/swift-sdk/Internal/api-client/ApiClientProtocol.swift +++ b/swift-sdk/Internal/api-client/ApiClientProtocol.swift @@ -48,6 +48,8 @@ protocol ApiClientProtocol: AnyObject { func getEmbeddedMessages() -> Pending + func getEmbeddedMessages(placementIds: [Int]?) -> Pending + @discardableResult func track(embeddedMessageReceived message: IterableEmbeddedMessage) -> Pending @discardableResult func track(embeddedMessageClick message: IterableEmbeddedMessage, buttonIdentifier: String?, clickedUrl: String) -> Pending diff --git a/swift-sdk/Internal/api-client/Request/RequestCreator.swift b/swift-sdk/Internal/api-client/Request/RequestCreator.swift index 9a14fba24..ca1b711ef 100644 --- a/swift-sdk/Internal/api-client/Request/RequestCreator.swift +++ b/swift-sdk/Internal/api-client/Request/RequestCreator.swift @@ -423,7 +423,7 @@ struct RequestCreator { // MARK: - Embedded Messaging Request Calls - func createGetEmbeddedMessagesRequest() -> Result { + func createGetEmbeddedMessagesRequest(placementIds: [Int]? = []) -> Result { if case .none = auth.emailOrUserId { ITBError(Self.authMissingMessage) return .failure(IterableError.general(description: Self.authMissingMessage)) @@ -437,6 +437,11 @@ struct RequestCreator { args[JsonKey.Embedded.packageName] = packageName } + if let placementIds = placementIds, + !placementIds.isEmpty { + args[JsonKey.placementIds] = placementIds + } + setCurrentUser(inDict: &args) return .success(.get(createGetRequest(forPath: Const.Path.getEmbeddedMessages, withArgs: args as! [String: String]))) @@ -542,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() }) 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 b9f0d637f..98b3914b6 100644 --- a/tests/unit-tests/EmbeddedManagerTests.swift +++ b/tests/unit-tests/EmbeddedManagerTests.swift @@ -69,7 +69,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") @@ -105,6 +105,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") @@ -352,7 +386,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 } @@ -367,13 +402,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) }