diff --git a/.github/workflows/update_versions.yml b/.github/workflows/update_versions.yml
new file mode 100644
index 0000000..4ef3c68
--- /dev/null
+++ b/.github/workflows/update_versions.yml
@@ -0,0 +1,47 @@
+# This is a basic workflow that is manually triggered
+
+name: Update Versions
+
+# Controls when the action will run. Workflow runs when manually triggered using the UI
+# or API.
+on:
+ workflow_dispatch:
+ # Inputs the workflow accepts.
+ inputs:
+ version:
+ description: 'New version to use for the Target extension. Example: 4.0.2'
+ required: true
+
+ branch:
+ description: 'Branch to be used when updating versions'
+ required: true
+
+ core-dependency:
+ description: 'If a version is provided, update AEPCore dependency in podspec and Package.swift'
+ required: false
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ update-versions:
+ runs-on: macos-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v3.1.0
+ with:
+ ref: ${{ github.event.inputs.branch }}
+
+ - name: Update AEPTarget
+ run: (sh ./Script/update-versions.sh -n Target -v ${{ github.event.inputs.version }} -d "AEPCore ${{ github.event.inputs.core-dependency }}")
+
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@v4.2.3
+ with:
+ token: ${{ github.token }}
+ commit-message: Updating version to ${{ github.event.inputs.version }}.
+ branch: version-${{ github.event.inputs.version }}-update
+ delete-branch: true
+ title: Updating version to ${{ github.event.inputs.version }}
+ body: Updating version to ${{ github.event.inputs.version }}
diff --git a/AEPTarget.podspec b/AEPTarget.podspec
index e077721..de3e7da 100644
--- a/AEPTarget.podspec
+++ b/AEPTarget.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "AEPTarget"
- s.version = "4.0.1"
+ s.version = "4.0.2"
s.summary = "Experience Platform Target extension for Adobe Experience Platform Mobile SDK. Written and maintained by Adobe."
s.description = <<-DESC
The Experience Platform Target extension provides APIs that allow use of the Target product in the Adobe Experience Platform SDK.
diff --git a/AEPTarget.xcodeproj/project.pbxproj b/AEPTarget.xcodeproj/project.pbxproj
index 8a5babd..613cc0e 100644
--- a/AEPTarget.xcodeproj/project.pbxproj
+++ b/AEPTarget.xcodeproj/project.pbxproj
@@ -1174,7 +1174,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
- MARKETING_VERSION = 4.0.1;
+ MARKETING_VERSION = 4.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.adobe.aep.target;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1205,7 +1205,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
- MARKETING_VERSION = 4.0.1;
+ MARKETING_VERSION = 4.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.adobe.aep.target;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/AEPTarget.xcodeproj/xcshareddata/xcschemes/AEPTargetDemoApp.xcscheme b/AEPTarget.xcodeproj/xcshareddata/xcschemes/AEPTargetDemoApp.xcscheme
index cc228d1..d476db1 100644
--- a/AEPTarget.xcodeproj/xcshareddata/xcschemes/AEPTargetDemoApp.xcscheme
+++ b/AEPTarget.xcodeproj/xcshareddata/xcschemes/AEPTargetDemoApp.xcscheme
@@ -44,6 +44,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ enableAddressSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -60,6 +61,13 @@
ReferencedContainer = "container:AEPTarget.xcodeproj">
+
+
+
+
String {
+ guard let eventHubData = eventHubData else {
+ return ""
+ }
+
+ let coreVersion = eventHubData[TargetConstants.EventHub.SharedState.Keys.VERSION] as? String ?? "unknown"
+
+ // sdkVersion is a combination of Mobile Core+Target SDK version
+ return "\(coreVersion)+\(TargetConstants.EXTENSION_VERSION)"
+ }
+
+ private func getSdkInfo(eventHubData: [String: Any]?) -> String {
+ let sdkBase = TargetConstants.HEADER_X_EXC_SDK_BASE_TARGET_MOBILE_IOS
+
+ guard let eventHubData = eventHubData else {
+ return sdkBase
+ }
+
+ guard let wrapper = eventHubData[TargetConstants.EventHub.SharedState.Keys.WRAPPER] as? [String: Any],
+ let wrapperFriendlyName = wrapper[TargetConstants.EventHub.SharedState.Keys.WRAPPER_FRIENDLY_NAME] as? String,
+ wrapperFriendlyName != "None"
+ else {
+ return sdkBase
+ }
+
+ // sdkInfo is of the format AdobeTargetMobile-iOS-
+ return "\(sdkBase)-\(wrapperFriendlyName)"
+ }
+
/// Prepares for the target requests and checks whether a target request can be sent.
/// - returns: error indicating why the request can't be sent, nil otherwise
private func prepareForTargetRequest() -> String? {
@@ -667,8 +696,6 @@ public class Target: NSObject, Extension {
return "Failed to generate request parameter(JSON) for target delivery API call"
}
- let headers = [TargetConstants.HEADER_CONTENT_TYPE: TargetConstants.HEADER_CONTENT_TYPE_JSON]
-
guard let clientCode = targetState.clientCode else {
return "Missing client code"
}
@@ -677,7 +704,13 @@ public class Target: NSObject, Extension {
return "Failed to generate the url for target API call"
}
+ let eventHubSharedState = getSharedState(extensionName: TargetConstants.EventHub.EXTENSION_NAME, event: event)?.value
let timeout = targetState.networkTimeout
+ let headers = [
+ TargetConstants.HEADER_CONTENT_TYPE: TargetConstants.HEADER_CONTENT_TYPE_JSON,
+ TargetConstants.HEADER_X_EXC_SDK: getSdkInfo(eventHubData: eventHubSharedState),
+ TargetConstants.HEADER_X_EXC_SDK_VERSION: getSdkVersion(eventHubData: eventHubSharedState),
+ ]
// https://developers.adobetarget.com/api/delivery-api/#tag/Delivery-API
let request = NetworkRequest(url: url, httpMethod: .post, connectPayload: requestJson, httpHeaders: headers, connectTimeout: timeout, readTimeout: timeout)
@@ -903,8 +936,13 @@ public class Target: NSObject, Extension {
return
}
+ let eventHubSharedState = getSharedState(extensionName: TargetConstants.EventHub.EXTENSION_NAME, event: event)?.value
let timeout = targetState.networkTimeout
- let headers = [TargetConstants.HEADER_CONTENT_TYPE: TargetConstants.HEADER_CONTENT_TYPE_JSON]
+ let headers = [
+ TargetConstants.HEADER_CONTENT_TYPE: TargetConstants.HEADER_CONTENT_TYPE_JSON,
+ TargetConstants.HEADER_X_EXC_SDK: getSdkInfo(eventHubData: eventHubSharedState),
+ TargetConstants.HEADER_X_EXC_SDK_VERSION: getSdkVersion(eventHubData: eventHubSharedState),
+ ]
// https://developers.adobetarget.com/api/delivery-api/#tag/Delivery-API
let request = NetworkRequest(url: url, httpMethod: .post, connectPayload: requestJson, httpHeaders: headers, connectTimeout: timeout, readTimeout: timeout)
@@ -916,8 +954,9 @@ public class Target: NSObject, Extension {
Log.debug(label: Target.LOG_TAG, "handleRawRequest - Target response is received with code: \(connection.responseCode ?? -1) and data: \(connection.responseString ?? "").")
self.targetState.updateSessionTimestamp()
self.processTargetRawResponse(event: event, isContentRequest: isContentRequest, connection: connection)
+
+ self.startEvents()
}
- startEvents()
}
/// Processes the network response after the Target delivery API call for raw request.
diff --git a/AEPTarget/Sources/TargetConstants.swift b/AEPTarget/Sources/TargetConstants.swift
index fa70e3a..ae41e3e 100644
--- a/AEPTarget/Sources/TargetConstants.swift
+++ b/AEPTarget/Sources/TargetConstants.swift
@@ -15,14 +15,17 @@ import Foundation
enum TargetConstants {
static let EXTENSION_NAME = "com.adobe.module.target"
static let FRIENDLY_NAME = "Target"
- static let EXTENSION_VERSION = "4.0.1"
+ static let EXTENSION_VERSION = "4.0.2"
static let DATASTORE_NAME = EXTENSION_NAME
static let DEFAULT_SESSION_TIMEOUT: Int = 30 * 60 // 30 mins
static let DELIVERY_API_URL_BASE = "https://%@/rest/v1/delivery/?client=%@&sessionId=%@"
static let EDGE_HOST_BASE = "mboxedge%@"
static let API_URL_HOST_BASE = "%@.tt.omtrdc.net"
static let HEADER_CONTENT_TYPE = "Content-Type"
+ static let HEADER_X_EXC_SDK = "X-EXC-SDK"
+ static let HEADER_X_EXC_SDK_VERSION = "X-EXC-SDK-Version"
static let HEADER_CONTENT_TYPE_JSON = "application/json"
+ static let HEADER_X_EXC_SDK_BASE_TARGET_MOBILE_IOS = "AdobeTargetMobile-iOS"
static let A4T_ACTION_NAME = "AnalyticsForTarget"
static let MAP_TO_CONTEXT_DATA_KEYS: [String: String] = [
@@ -234,6 +237,17 @@ enum TargetConstants {
}
}
+ enum EventHub {
+ static let EXTENSION_NAME = "com.adobe.module.eventhub"
+ enum SharedState {
+ enum Keys {
+ static let VERSION = "version"
+ static let WRAPPER = "wrapper"
+ static let WRAPPER_FRIENDLY_NAME = "friendlyName"
+ }
+ }
+ }
+
enum Identity {
static let EXTENSION_NAME = "com.adobe.module.identity"
enum SharedState {
diff --git a/AEPTarget/Sources/TargetState.swift b/AEPTarget/Sources/TargetState.swift
index a865621..92c120f 100644
--- a/AEPTarget/Sources/TargetState.swift
+++ b/AEPTarget/Sources/TargetState.swift
@@ -15,11 +15,31 @@ import Foundation
/// Represents the state of the `Target` extension
class TargetState {
- private(set) var prefetchedMboxJsonDicts = [String: [String: Any]]()
- private(set) var loadedMboxJsonDicts = [String: [String: Any]]()
- private(set) var notifications = [Notification]()
+ private let queue: DispatchQueue = .init(label: "com.adobe.targetstate.queue")
- private(set) var storedConfigurationSharedState: [String: Any]?
+ private var _prefetchedMboxJsonDicts: [String: [String: Any]] = [:]
+ private(set) var prefetchedMboxJsonDicts: [String: [String: Any]] {
+ get { queue.sync { self._prefetchedMboxJsonDicts } }
+ set { queue.async { self._prefetchedMboxJsonDicts = newValue } }
+ }
+
+ private var _loadedMboxJsonDicts: [String: [String: Any]] = [:]
+ private(set) var loadedMboxJsonDicts: [String: [String: Any]] {
+ get { queue.sync { self._loadedMboxJsonDicts } }
+ set { queue.async { self._loadedMboxJsonDicts = newValue } }
+ }
+
+ private var _notifications: [Notification] = []
+ private(set) var notifications: [Notification] {
+ get { queue.sync { self._notifications } }
+ set { queue.async { self._notifications = newValue } }
+ }
+
+ private var _storedConfigurationSharedState: [String: Any]?
+ private(set) var storedConfigurationSharedState: [String: Any]? {
+ get { queue.sync { self._storedConfigurationSharedState } }
+ set { queue.async { self._storedConfigurationSharedState = newValue } }
+ }
private(set) var thirdPartyId: String?
private(set) var tntId: String?
diff --git a/AEPTarget/Tests/IntegrationTests/TargetIntegrationTests.swift b/AEPTarget/Tests/IntegrationTests/TargetIntegrationTests.swift
index 0658fa9..9b1f240 100644
--- a/AEPTarget/Tests/IntegrationTests/TargetIntegrationTests.swift
+++ b/AEPTarget/Tests/IntegrationTests/TargetIntegrationTests.swift
@@ -185,6 +185,12 @@ class TargetIntegrationTests: XCTestCase {
mockNetworkService.mock { request in
Log.debug(label: self.T_LOG_TAG, "request url is: \(request.url.absoluteString)")
if request.url.absoluteString.contains("https://amsdk.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
if let payloadDictionary = try? JSONSerialization.jsonObject(with: request.connectPayload, options: .allowFragments) as? [String: Any]
{
Log.debug(label: self.T_LOG_TAG, "request payload is: \n \(self.prettify(payloadDictionary))")
@@ -407,6 +413,12 @@ class TargetIntegrationTests: XCTestCase {
ServiceProvider.shared.networkService = mockNetworkService
mockNetworkService.mock { request in
if request.url.absoluteString.contains("https://amsdk.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
return (data: responseString.data(using: .utf8), response: validResponse, error: nil)
}
return nil
@@ -1093,6 +1105,12 @@ class TargetIntegrationTests: XCTestCase {
ServiceProvider.shared.networkService = mockNetworkService
mockNetworkService.mock { request in
if request.url.absoluteString.contains("https://amsdk.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
let connectPayloadString = String(decoding: request.connectPayload, as: UTF8.self)
if connectPayloadString.contains("ADCKKBC") {
targetRequestExpectation.fulfill()
@@ -1219,6 +1237,12 @@ class TargetIntegrationTests: XCTestCase {
ServiceProvider.shared.networkService = mockNetworkService
mockNetworkService.mock { request in
if request.url.absoluteString.contains("https://amsdk.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
let connectPayloadString = String(decoding: request.connectPayload, as: UTF8.self)
if connectPayloadString.contains("ADCKKBC") {
targetRequestExpectation.fulfill()
@@ -1662,6 +1686,12 @@ class TargetIntegrationTests: XCTestCase {
ServiceProvider.shared.networkService = mockNetworkService
mockNetworkService.mock { request in
if request.url.absoluteString.contains("https://acopprod3.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
return (data: responseString.data(using: .utf8), response: validResponse, error: nil)
}
return nil
@@ -1802,6 +1832,12 @@ class TargetIntegrationTests: XCTestCase {
ServiceProvider.shared.networkService = mockNetworkService
mockNetworkService.mock { request in
if request.url.absoluteString.contains("https://acopprod3.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
return (data: responseString.data(using: .utf8), response: validResponse, error: nil)
}
return nil
@@ -1954,6 +1990,12 @@ class TargetIntegrationTests: XCTestCase {
ServiceProvider.shared.networkService = mockNetworkService
mockNetworkService.mock { request in
if request.url.absoluteString.contains("https://acopprod3.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
return (data: responseString.data(using: .utf8), response: validResponse, error: nil)
}
@@ -2096,6 +2138,12 @@ class TargetIntegrationTests: XCTestCase {
}
if request.url.absoluteString.contains("https://mboxedge35.tt.omtrdc.net/rest/v1/delivery/?client=acopprod3&sessionId=") {
+ // verify request headers
+ let coreVersion = self.getLastValidSharedState("com.adobe.module.eventhub")?.value?["version"] ?? "unknown"
+ let requestHeaders: [String: String] = request.httpHeaders
+ XCTAssertEqual("AdobeTargetMobile-iOS", requestHeaders["X-EXC-SDK"])
+ XCTAssertEqual("\(coreVersion)+\(TargetTestConstants.EXTENSION_VERSION)", requestHeaders["X-EXC-SDK-Version"])
+
targetNotificationExpectation.fulfill()
return nil
}
diff --git a/AEPTarget/Tests/TestHelpers/TargetTestConstants.swift b/AEPTarget/Tests/TestHelpers/TargetTestConstants.swift
index 2493a71..3d8d7b8 100644
--- a/AEPTarget/Tests/TestHelpers/TargetTestConstants.swift
+++ b/AEPTarget/Tests/TestHelpers/TargetTestConstants.swift
@@ -11,6 +11,7 @@
*/
enum TargetTestConstants {
+ static let EXTENSION_VERSION = "4.0.2"
// preview parameters
static let PREVIEW_MESSAGE_ID = "target-preview-message-id"
static let PREVIEW_PARAMETERS = "at_preview_params"
diff --git a/Makefile b/Makefile
index a593c6d..b4aa3d9 100644
--- a/Makefile
+++ b/Makefile
@@ -87,3 +87,7 @@ test-podspec:
pod-lint:
(pod lib lint --allow-warnings --verbose --swift-version=5.1)
+
+# used to test update-versions.sh script locally
+test-update-version:
+ (sh ./Script/update-versions.sh -n Target -v 9.9.9 -d "AEPCore 9.9.3")
diff --git a/Podfile.lock b/Podfile.lock
index e162a3e..f981056 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -5,17 +5,17 @@ PODS:
- AEPAssurance (4.0.0):
- AEPCore (>= 4.0.0)
- AEPServices (>= 4.0.0)
- - AEPCore (4.0.0):
+ - AEPCore (4.1.0):
- AEPRulesEngine (>= 4.0.0)
- - AEPServices (>= 4.0.0)
- - AEPIdentity (4.0.0):
- - AEPCore (>= 4.0.0)
- - AEPLifecycle (4.0.0):
- - AEPCore (>= 4.0.0)
+ - AEPServices (>= 4.1.0)
+ - AEPIdentity (4.1.0):
+ - AEPCore (>= 4.1.0)
+ - AEPLifecycle (4.1.0):
+ - AEPCore (>= 4.1.0)
- AEPRulesEngine (4.0.0)
- - AEPServices (4.0.0)
- - AEPSignal (4.0.0):
- - AEPCore (>= 4.0.0)
+ - AEPServices (4.1.0)
+ - AEPSignal (4.1.0):
+ - AEPCore (>= 4.1.0)
- SwiftLint (0.52.0)
- SwiftyJSON (5.0.1)
@@ -45,12 +45,12 @@ SPEC REPOS:
SPEC CHECKSUMS:
AEPAnalytics: a510eb9653fac7f913965ad4291c8d51f74ffdcd
AEPAssurance: 4fa3138ddd7308c1f9923570f4d2b0b8526a916f
- AEPCore: dd7cd69696c768c610e6adc0307032985a381c7e
- AEPIdentity: 45ee1c3717e08ff3ca60930caf4a869d60d7bf08
- AEPLifecycle: 59be1b5381d8ec4939ece43516ea7d2de4aaba65
+ AEPCore: 20fb832a7467b25ca4aca186c0a5a1e3c0c6abc3
+ AEPIdentity: 88671626d6043a488896ee7d71483a8bcec80739
+ AEPLifecycle: 97693ea99ef9deb818b726a4e429ef96abb1353e
AEPRulesEngine: 458450a34922823286ead045a0c2bd8c27e224c6
- AEPServices: ca493988df250d84fda050124ff7549bcc43c65f
- AEPSignal: b2b332adf4d8a9af6a1b57f5dd8c2e1ea6d5c112
+ AEPServices: d94555679870311d2f1391c5d7a5de590fd1f3c0
+ AEPSignal: 9152e68bae462276f57ac63666e879cc7ff7c302
SwiftLint: 13280e21cdda6786ad908dc6e416afe5acd1fcb7
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
diff --git a/Script/update-versions.sh b/Script/update-versions.sh
new file mode 100755
index 0000000..af6ab6f
--- /dev/null
+++ b/Script/update-versions.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+# make this script executable from terminal:
+# chmod 755 update-versions.sh
+
+set -e # Any subsequent(*) commands which fail will cause the shell script to exit immediately
+
+ROOT_DIR=$(git rev-parse --show-toplevel)
+LINE="================================================================================"
+VERSION_REGEX="[0-9]+\.[0-9]+\.[0-9]+"
+DEPENDENCIES=none
+
+# make a "dictionary" to help us find the correct spm repo per dependency (if necessary)
+# IMPORTANT - this will be used in a regex search so escape special chars
+# usage :
+# getRepo AEPCore
+
+declare "repos_AEPCore=https:\/\/github\.com\/adobe\/aepsdk-core-ios\.git"
+
+getRepo() {
+ local extensionName=$1
+ local url="repos_$extensionName"
+ echo "${!url}"
+}
+
+help()
+{
+ echo ""
+ echo "Usage: $0 -n EXTENSION_NAME -v NEW_VERSION -d \"PODSPEC_DEPENDENCY_1, PODSPEC_DEPENDENCY_2\""
+ echo ""
+ echo -e " -n\t- Name of the extension getting a version update. \n\t Example: Edge, Analytics\n"
+ echo -e " -v\t- New version to use for the extension. \n\t Example: 4.0.2\n"
+ echo -e " -d (optional)\t- Dependency(ies) that require updating in the extension's podspec and Package.swift file. \n\t Example: -d \"AEPCore 4.0.2\" (update the dependency on AEPCore to version 4.0.2 or newer)\n"
+ exit 1 # Exit script after printing help
+}
+
+while getopts "n:v:d:" opt
+do
+ case "$opt" in
+ n ) NAME="$OPTARG" ;;
+ v ) NEW_VERSION="$OPTARG" ;;
+ d ) DEPENDENCIES="$OPTARG" ;;
+ ? ) help ;; # Print help in case parameter is non-existent
+ esac
+done
+
+# Print help in case parameters are empty
+if [ -z "$NAME" ] || [ -z "$NEW_VERSION" ]
+then
+ echo "********** USAGE ERROR **********"
+ echo "Some or all of the parameters are empty. See usage below:";
+ help
+fi
+
+PODSPEC_FILE=$ROOT_DIR"/AEP"$NAME.podspec
+SPM_FILE=$ROOT_DIR/Package.swift
+
+# Begin script in case all parameters are correct
+echo ""
+echo "$LINE"
+echo "Changing version of AEP$NAME to $NEW_VERSION with the following minimum version dependencies: $DEPENDENCIES"
+echo "$LINE"
+
+# Replace extension version in podspec
+echo "Changing value of 's.version' to '$NEW_VERSION' in '$PODSPEC_FILE'"
+sed -i '' -E "/^ *s.version/{s/$VERSION_REGEX/$NEW_VERSION/;}" $PODSPEC_FILE
+
+# Replace dependencies in podspec and Package.swift
+if [ "$DEPENDENCIES" != "none" ]; then
+ IFS=","
+ dependenciesArray=($(echo "$DEPENDENCIES"))
+
+ IFS=" "
+ for dependency in "${dependenciesArray[@]}"; do
+ dependencyArray=(${dependency// / })
+ dependencyName=${dependencyArray[0]}
+ dependencyVersion=${dependencyArray[1]}
+
+ if [ "$dependencyVersion" != "" ]; then
+ echo "Changing value of 's.dependency' for '$dependencyName' to '>= $dependencyVersion' in '$PODSPEC_FILE'"
+ sed -i '' -E "/^ *s.dependency +'$dependencyName'/{s/$VERSION_REGEX/$dependencyVersion/;}" $PODSPEC_FILE
+
+ spmRepoUrl=$(getRepo $dependencyName)
+ if [ "$spmRepoUrl" != "" ]; then
+ echo "Changing value of '.upToNextMajor(from:)' for '$spmRepoUrl' to '$dependencyVersion' in '$SPM_FILE'"
+ sed -i '' -E "/$spmRepoUrl\", \.upToNextMajor/{s/$VERSION_REGEX/$dependencyVersion/;}" $SPM_FILE
+ fi
+ fi
+ done
+fi
+
+# Replace version in Constants file
+CONSTANTS_FILE=$ROOT_DIR"/AEP$NAME/Sources/"$NAME"Constants.swift"
+echo "Changing value of 'EXTENSION_VERSION' to '$NEW_VERSION' in '$CONSTANTS_FILE'"
+sed -i '' -E "/^ +static let EXTENSION_VERSION/{s/$VERSION_REGEX/$NEW_VERSION/;}" $CONSTANTS_FILE
+
+# Replace version in TestConstants file
+TEST_CONSTANTS_FILE=$ROOT_DIR"/AEP$NAME/Tests/TestHelpers/"$NAME"TestConstants.swift"
+echo "Changing value of 'EXTENSION_VERSION' to '$NEW_VERSION' in '$TEST_CONSTANTS_FILE'"
+sed -i '' -E "/^ +static let EXTENSION_VERSION/{s/$VERSION_REGEX/$NEW_VERSION/;}" $TEST_CONSTANTS_FILE
+
+# Replace marketing versions in project.pbxproj
+PROJECT_PBX_FILE=$ROOT_DIR"/AEP$NAME.xcodeproj/project.pbxproj"
+echo "Changing value of 'MARKETING_VERSION' to '$NEW_VERSION' in '$PROJECT_PBX_FILE'"
+sed -i '' -E "/^\t+MARKETING_VERSION = /{s/$VERSION_REGEX/$NEW_VERSION/;}" $PROJECT_PBX_FILE
\ No newline at end of file