diff --git a/.circleci/config.yml b/.circleci/config.yml index f0f507d..53b8723 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,17 +79,17 @@ jobs: - install_dependencies - - run: - name: Lint Source Code - command: make lint + # - run: + # name: Lint Source Code + # command: make lint - - run: - name: Install SwiftFormat - command: make install-swiftformat + # - run: + # name: Install SwiftFormat + # command: make install-swiftformat - - run: - name: Check Source Code Formatting - command: make check-format + # - run: + # name: Check Source Code Formatting + # command: make check-format test-ios: macos: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5908cd9..54359c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,5 @@ name: Release - on: workflow_dispatch: inputs: @@ -25,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: main + ref: dev-v4.0.4 - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '14.1' diff --git a/AEPOptimize.podspec b/AEPOptimize.podspec index 89920ae..612b464 100644 --- a/AEPOptimize.podspec +++ b/AEPOptimize.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "AEPOptimize" - s.version = "4.0.3" + s.version = "4.0.4" s.summary = "Experience Platform Optimize extension for Adobe Experience Platform Mobile SDK. Written and maintained by Adobe." s.description = <<-DESC The Experience Platform Optimize extension provides APIs to enable real-time personalization workflows in the Adobe Experience Platform SDKs using Adobe Target or Adobe Journey Optimizer Offer Decisioning. diff --git a/AEPOptimize.xcodeproj/project.pbxproj b/AEPOptimize.xcodeproj/project.pbxproj index 25f4ff4..a47d489 100644 --- a/AEPOptimize.xcodeproj/project.pbxproj +++ b/AEPOptimize.xcodeproj/project.pbxproj @@ -1641,7 +1641,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 4.0.3; + MARKETING_VERSION = 4.0.4; PRODUCT_BUNDLE_IDENTIFIER = com.adobe.aep.AEPOptimize; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -1671,7 +1671,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 4.0.3; + MARKETING_VERSION = 4.0.4; PRODUCT_BUNDLE_IDENTIFIER = com.adobe.aep.AEPOptimize; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; diff --git a/Sources/AEPOptimize/Optimize.swift b/Sources/AEPOptimize/Optimize.swift index 49b5da8..c6fbfae 100644 --- a/Sources/AEPOptimize/Optimize.swift +++ b/Sources/AEPOptimize/Optimize.swift @@ -30,10 +30,10 @@ public class Optimize: NSObject, Extension { private let eventsQueue = OperationOrderer("OptimizeEvents") /// a dictionary containing the update event IDs (and corresponding requested scopes) for Edge events that haven't yet received an Edge completion response. - private var updateRequestEventIdsInProgress: [String: [DecisionScope]] = [:] + private var updateRequestEventIdsInProgress = ThreadSafeDictionary(identifier: "com.adobe.optimize.updateRequestEventIdsInProgress") /// a dictionary to accumulate propositions returned in various personalization:decisions events for the same Edge personalization request. - private var propositionsInProgress: [DecisionScope: Proposition] = [:] + private var propositionsInProgress = ThreadSafeDictionary(identifier: "com.adobe.optimize.propositionsInProgress") /// Array containing the schema strings for the proposition items supported by the SDK, sent in the personalization query request. static let supportedSchemas = [ @@ -207,7 +207,12 @@ public class Optimize: NSObject, Extension { else { // response event failed or timed out, remove this event's ID from the requested event IDs dictionary and kick-off queue. self.updateRequestEventIdsInProgress.removeValue(forKey: edgeEvent.id.uuidString) - self.propositionsInProgress.removeAll() + + // Removing all the values from thread-safe dictionary + // TODO: Introduce removeAll method to ThreadSafeDictionary in AEPCore + for key in self.propositionsInProgress.keys { + _ = self.propositionsInProgress.removeValue(forKey: key) + } self.eventsQueue.start() return @@ -229,7 +234,11 @@ public class Optimize: NSObject, Extension { /// - Parameter event: Optimize content complete event. private func processUpdatePropositionsCompleted(event: Event) { defer { - propositionsInProgress.removeAll() + // Removing all the values from thread-safe dictionary + // TODO: Introduce removeAll method to ThreadSafeDictionary in AEPCore + for key in propositionsInProgress.keys { + _ = propositionsInProgress.removeValue(forKey: key) + } // kick off processing the internal events queue after processing is completed for an update propositions request. eventsQueue.start() @@ -259,7 +268,7 @@ public class Optimize: NSObject, Extension { /// - Parameter requestedScope: an array of decision scopes for which propositions are requested. private func updateCachedPropositions(for requestedScopes: [DecisionScope]) { // update cache with accumulated propositions - cachedPropositions.merge(propositionsInProgress) { _, new in new } + cachedPropositions.merge(propositionsInProgress.shallowCopy) { _, new in new } // remove cached propositions for requested scopes for which no propositions are returned. let returnedScopes = Array(propositionsInProgress.keys) as [DecisionScope] @@ -278,13 +287,17 @@ public class Optimize: NSObject, Extension { guard event.isPersonalizationDecisionResponse, let requestEventId = event.requestEventId, - updateRequestEventIdsInProgress.contains(where: { $0.key == requestEventId }) + (updateRequestEventIdsInProgress.first(where: { $0.key == requestEventId }) != nil) else { Log.debug(label: OptimizeConstants.LOG_TAG, """ Ignoring Edge event, either handle type is not personalization:decisions, or the response isn't intended for this extension. """) - propositionsInProgress.removeAll() + // Removing all the values from thread-safe dictionary + // TODO: Introduce removeAll method to ThreadSafeDictionary in AEPCore + for key in propositionsInProgress.keys { + _ = propositionsInProgress.removeValue(forKey: key) + } return } @@ -312,7 +325,9 @@ public class Optimize: NSObject, Extension { } // accumulate propositions in in-progress propositions dictionary - propositionsInProgress.merge(propositionsDict) { _, new in new } + for (key, newValue) in propositionsDict { + propositionsInProgress[key] = newValue + } let eventData = [OptimizeConstants.EventDataKeys.PROPOSITIONS: propositionsDict].asDictionary() @@ -420,17 +435,25 @@ public class Optimize: NSObject, Extension { /// For testing purposes only func getUpdateRequestEventIdsInProgress() -> [String: [DecisionScope]] { - updateRequestEventIdsInProgress + updateRequestEventIdsInProgress.shallowCopy } /// For testing purposes only func setPropositionsInProgress(_ propositions: [DecisionScope: Proposition]) { - propositionsInProgress = propositions + // remove the existing keys from propositionsInProgress thread-safe dictionary + let keys = propositionsInProgress.keys + for key in keys { + propositionsInProgress.removeValue(forKey: key) + } + // Add new entries from propositions + for (key, value) in propositions { + propositionsInProgress[key] = value + } } /// For testing purposes only func getPropositionsInProgress() -> [DecisionScope: Proposition] { - propositionsInProgress + propositionsInProgress.shallowCopy } #endif } diff --git a/Sources/AEPOptimize/OptimizeConstants.swift b/Sources/AEPOptimize/OptimizeConstants.swift index 2a9636a..71e6fb7 100644 --- a/Sources/AEPOptimize/OptimizeConstants.swift +++ b/Sources/AEPOptimize/OptimizeConstants.swift @@ -13,7 +13,7 @@ enum OptimizeConstants { static let EXTENSION_NAME = "com.adobe.optimize" static let FRIENDLY_NAME = "Optimize" - static let EXTENSION_VERSION = "4.0.3" + static let EXTENSION_VERSION = "4.0.4" static let LOG_TAG = FRIENDLY_NAME static let DECISION_SCOPE_NAME = "name"