diff --git a/.github/workflows/build-secondary-platforms.yml b/.github/workflows/build-secondary-platforms.yml
index 63ef85530..2d9e10f30 100644
--- a/.github/workflows/build-secondary-platforms.yml
+++ b/.github/workflows/build-secondary-platforms.yml
@@ -23,7 +23,7 @@ jobs:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Setup specified simulator
- uses: futureware-tech/simulator-action@v4
+ uses: futureware-tech/simulator-action@v5
id: simulator
with:
model: iPhone 16 Pro
diff --git a/.github/workflows/cross-platform-tests.yml b/.github/workflows/cross-platform-tests.yml
index b93578acb..2ade257ed 100644
--- a/.github/workflows/cross-platform-tests.yml
+++ b/.github/workflows/cross-platform-tests.yml
@@ -40,7 +40,7 @@ jobs:
run: ./gradlew runIos
- name: Archive Test Results
if: always()
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: ios-test-results
path: Users/runner/Library/Developer/Xcode/DerivedData
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index e4c5ba753..311bc55f8 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -99,7 +99,7 @@ jobs:
- name: Upload artifacts on failure
if: failure()
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: integration-test-artifacts
path: ${{ env.WORKING_DIRECTORY }}/artifacts/
diff --git a/.github/workflows/native-tests.yml b/.github/workflows/native-tests.yml
index a4d5e3d2f..b2e3a3881 100644
--- a/.github/workflows/native-tests.yml
+++ b/.github/workflows/native-tests.yml
@@ -32,7 +32,7 @@ jobs:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Setup specified simulator
- uses: futureware-tech/simulator-action@v4
+ uses: futureware-tech/simulator-action@v5
id: simulator
with:
model: ${{ matrix.device }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f3f7ecf0..cf2f41ead 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,12 @@ All notable changes to the mParticle Apple SDK (core and integration kits) are d
---
+# [8.44.0](https://github.com/mParticle/mparticle-apple-sdk/compare/v8.43.1...v8.44.0) (2026-02-19)
+
+### Bug Fixes
+
+- fix: SceneDelegate Protocol Objc Selector Issue (#603) ([99364bfb](https://github.com/mParticle/mparticle-apple-sdk/commit/99364bfbf62401c6d53b195b63a21576baaf354b))
+
# [8.43.1](https://github.com/mParticle/mparticle-apple-sdk/compare/v8.42.2...v8.43.1) (2026-02-16)
### Core
diff --git a/Framework/Info.plist b/Framework/Info.plist
index c9656ac5d..3a210b676 100644
--- a/Framework/Info.plist
+++ b/Framework/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 8.43.1
+ 8.44.0
CFBundleSignature
????
CFBundleVersion
diff --git a/RNExample/package-lock.json b/RNExample/package-lock.json
index fa4f77d0e..49b8f36a9 100644
--- a/RNExample/package-lock.json
+++ b/RNExample/package-lock.json
@@ -6382,18 +6382,14 @@
"license": "MIT"
},
"node_modules/fast-xml-parser": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz",
- "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==",
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.4.tgz",
+ "integrity": "sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/naturalintelligence"
}
],
"license": "MIT",
diff --git a/SwiftExample/mParticleSwiftExample.xcodeproj/project.pbxproj b/SwiftExample/mParticleSwiftExample.xcodeproj/project.pbxproj
index 4223af168..1b57d6a7d 100644
--- a/SwiftExample/mParticleSwiftExample.xcodeproj/project.pbxproj
+++ b/SwiftExample/mParticleSwiftExample.xcodeproj/project.pbxproj
@@ -8,7 +8,7 @@
/* Begin PBXBuildFile section */
7E4B5EB92F338B9100B61C64 /* mParticle-Apple-SDK in Frameworks */ = {isa = PBXBuildFile; productRef = 7E4B5EB82F338B9100B61C64 /* mParticle-Apple-SDK */; };
- 7E4B5EBC2F338BA500B61C64 /* mParticle-Rokt in Frameworks */ = {isa = PBXBuildFile; productRef = 7E4B5EBB2F338BA500B61C64 /* mParticle-Rokt */; };
+ 7ED594AE2F46240D00AB6433 /* mParticle-Rokt in Frameworks */ = {isa = PBXBuildFile; productRef = 7ED594AD2F46240D00AB6433 /* mParticle-Rokt */; };
A10000010000000000000001 /* mParticleSwiftExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A10000020000000000000001 /* mParticleSwiftExampleApp.swift */; };
A10000010000000000000002 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A10000020000000000000002 /* ContentView.swift */; };
A10000010000000000000003 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A10000020000000000000003 /* Assets.xcassets */; };
@@ -31,7 +31,7 @@
files = (
7E4B5EB92F338B9100B61C64 /* mParticle-Apple-SDK in Frameworks */,
A10000010000000000000004 /* mParticle-Apple-SDK in Frameworks */,
- 7E4B5EBC2F338BA500B61C64 /* mParticle-Rokt in Frameworks */,
+ 7ED594AE2F46240D00AB6433 /* mParticle-Rokt in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -85,7 +85,7 @@
packageProductDependencies = (
A10000030000000000000001 /* mParticle-Apple-SDK */,
7E4B5EB82F338B9100B61C64 /* mParticle-Apple-SDK */,
- 7E4B5EBB2F338BA500B61C64 /* mParticle-Rokt */,
+ 7ED594AD2F46240D00AB6433 /* mParticle-Rokt */,
);
productName = mParticleSwiftExample;
productReference = A10000020000000000000006 /* mParticleSwiftExample.app */;
@@ -117,7 +117,7 @@
mainGroup = A10000050000000000000001;
packageReferences = (
7E4B5EB72F338B9100B61C64 /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */,
- 7E4B5EBA2F338BA500B61C64 /* XCRemoteSwiftPackageReference "mparticle-apple-integration-rokt" */,
+ 7ED594AC2F46240D00AB6433 /* XCRemoteSwiftPackageReference "mparticle-apple-integration-rokt" */,
);
productRefGroup = A10000050000000000000003 /* Products */;
projectDirPath = "";
@@ -367,7 +367,7 @@
minimumVersion = 8.4.0;
};
};
- 7E4B5EBA2F338BA500B61C64 /* XCRemoteSwiftPackageReference "mparticle-apple-integration-rokt" */ = {
+ 7ED594AC2F46240D00AB6433 /* XCRemoteSwiftPackageReference "mparticle-apple-integration-rokt" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mparticle-integrations/mparticle-apple-integration-rokt.git";
requirement = {
@@ -383,9 +383,9 @@
package = 7E4B5EB72F338B9100B61C64 /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */;
productName = "mParticle-Apple-SDK";
};
- 7E4B5EBB2F338BA500B61C64 /* mParticle-Rokt */ = {
+ 7ED594AD2F46240D00AB6433 /* mParticle-Rokt */ = {
isa = XCSwiftPackageProductDependency;
- package = 7E4B5EBA2F338BA500B61C64 /* XCRemoteSwiftPackageReference "mparticle-apple-integration-rokt" */;
+ package = 7ED594AC2F46240D00AB6433 /* XCRemoteSwiftPackageReference "mparticle-apple-integration-rokt" */;
productName = "mParticle-Rokt";
};
A10000030000000000000001 /* mParticle-Apple-SDK */ = {
diff --git a/SwiftExample/mParticleSwiftExample/ContentView.swift b/SwiftExample/mParticleSwiftExample/ContentView.swift
index e0e6db1b7..707ae2ecc 100644
--- a/SwiftExample/mParticleSwiftExample/ContentView.swift
+++ b/SwiftExample/mParticleSwiftExample/ContentView.swift
@@ -1,6 +1,8 @@
// swiftlint:disable file_length
import SwiftUI
import mParticle_Apple_SDK
+import mParticle_Rokt_Swift
+import Rokt_Widget
import AdSupport
import AppTrackingTransparency
@@ -95,6 +97,13 @@ struct ContentView: View {
title: "Display Rokt Overlay (auto close)",
action: selectOverlayPlacementAutoClose
)
+ ActionButton(
+ title: "Display Rokt with Event Subscription",
+ action: selectPlacementWithEventSubscription
+ )
+ NavigationLink("MPRoktLayout SwiftUI Example") {
+ RoktLayoutExampleView()
+ }
}
}
.listStyle(InsetGroupedListStyle())
@@ -202,6 +211,175 @@ struct RoktEmbeddedViewWrapper: UIViewRepresentable {
}
}
+// MARK: - MPRoktLayout SwiftUI Example
+
+struct RoktLayoutExampleView: View {
+ @State private var sdkTriggered = false
+ @State private var eventLog: [String] = []
+
+ let attributes: [String: String] = [
+ "email": "j.smith@example.com",
+ "firstname": "Jenny",
+ "lastname": "Smith",
+ "billingzipcode": "07762",
+ "confirmationref": "54321",
+ "sandbox": "true",
+ "mobile": "(555)867-5309"
+ ]
+
+ var body: some View {
+ ScrollView {
+ VStack(alignment: .leading, spacing: 16) {
+ Text("MPRoktLayout Example")
+ .font(.title2)
+ .fontWeight(.bold)
+ .padding(.horizontal)
+
+ Text("This demonstrates the SwiftUI-native MPRoktLayout component for embedding Rokt placements declaratively.")
+ .font(.subheadline)
+ .foregroundColor(.secondary)
+ .padding(.horizontal)
+
+ // Trigger button
+ Button {
+ sdkTriggered = true
+ eventLog.append("[\(formattedTime())] Triggered placement")
+ } label: {
+ Text(sdkTriggered ? "Placement Triggered" : "Trigger Rokt Placement")
+ .frame(maxWidth: .infinity)
+ .padding()
+ .background(sdkTriggered ? Color.gray : Color.blue)
+ .foregroundColor(.white)
+ .cornerRadius(10)
+ }
+ .disabled(sdkTriggered)
+ .padding(.horizontal)
+
+ // Reset button
+ if sdkTriggered {
+ Button {
+ sdkTriggered = false
+ eventLog.append("[\(formattedTime())] Reset placement")
+ } label: {
+ Text("Reset")
+ .frame(maxWidth: .infinity)
+ .padding()
+ .background(Color.orange)
+ .foregroundColor(.white)
+ .cornerRadius(10)
+ }
+ .padding(.horizontal)
+ }
+
+ // Rokt Layout - Embedded Placement
+ if sdkTriggered {
+ VStack(alignment: .leading, spacing: 8) {
+ Text("Embedded Placement")
+ .font(.headline)
+ .padding(.horizontal)
+
+ MPRoktLayout(
+ sdkTriggered: $sdkTriggered,
+ viewName: "RoktExperience",
+ locationName: "RoktEmbedded1",
+ attributes: attributes,
+ config: createRoktConfig(),
+ onEvent: { roktEvent in
+ handleRoktEvent(roktEvent)
+ }
+ ).roktLayout
+ .padding(.horizontal)
+ }
+ }
+
+ // Event Log
+ VStack(alignment: .leading, spacing: 8) {
+ Text("Event Log")
+ .font(.headline)
+ .padding(.horizontal)
+
+ if eventLog.isEmpty {
+ Text("No events yet. Trigger the placement to see events.")
+ .font(.caption)
+ .foregroundColor(.secondary)
+ .padding(.horizontal)
+ } else {
+ ForEach(eventLog.reversed(), id: \.self) { event in
+ Text(event)
+ .font(.system(.caption, design: .monospaced))
+ .padding(.horizontal)
+ }
+ }
+ }
+ .padding(.vertical)
+ .background(Color(.systemGray6))
+ .cornerRadius(10)
+ .padding(.horizontal)
+
+ Spacer()
+ }
+ .padding(.vertical)
+ }
+ .navigationTitle("MPRoktLayout")
+ .navigationBarTitleDisplayMode(.inline)
+ }
+
+ private func createRoktConfig() -> RoktConfig {
+ return RoktConfig.Builder().build()
+ }
+
+ private func handleRoktEvent(_ event: RoktEvent) {
+ let timestamp = formattedTime()
+
+ switch event {
+ case is RoktEvent.ShowLoadingIndicator:
+ eventLog.append("[\(timestamp)] Show Loading Indicator")
+
+ case is RoktEvent.HideLoadingIndicator:
+ eventLog.append("[\(timestamp)] Hide Loading Indicator")
+
+ case let placementReady as RoktEvent.PlacementReady:
+ eventLog.append("[\(timestamp)] Placement Ready - ID: \(placementReady.placementId ?? "unknown")")
+
+ case let placementInteractive as RoktEvent.PlacementInteractive:
+ eventLog.append("[\(timestamp)] Placement Interactive - ID: \(placementInteractive.placementId ?? "unknown")")
+
+ case let offerEngagement as RoktEvent.OfferEngagement:
+ eventLog.append("[\(timestamp)] Offer Engagement - ID: \(offerEngagement.placementId ?? "unknown")")
+
+ case let positiveEngagement as RoktEvent.PositiveEngagement:
+ eventLog.append("[\(timestamp)] Positive Engagement - ID: \(positiveEngagement.placementId ?? "unknown")")
+
+ case let firstPositiveEngagement as RoktEvent.FirstPositiveEngagement:
+ eventLog.append("[\(timestamp)] First Positive Engagement - ID: \(firstPositiveEngagement.placementId ?? "unknown")")
+
+ case let openUrl as RoktEvent.OpenUrl:
+ eventLog.append("[\(timestamp)] Open URL - \(openUrl.url)")
+
+ case let placementClosed as RoktEvent.PlacementClosed:
+ eventLog.append("[\(timestamp)] Placement Closed - ID: \(placementClosed.placementId ?? "unknown")")
+
+ case let placementCompleted as RoktEvent.PlacementCompleted:
+ eventLog.append("[\(timestamp)] Placement Completed - ID: \(placementCompleted.placementId ?? "unknown")")
+
+ case let placementFailure as RoktEvent.PlacementFailure:
+ eventLog.append("[\(timestamp)] Placement Failure - ID: \(placementFailure.placementId ?? "unknown")")
+
+ case let cartItem as RoktEvent.CartItemInstantPurchase:
+ eventLog.append("[\(timestamp)] Cart Item Purchase - \(cartItem.name ?? "unknown")")
+
+ default:
+ eventLog.append("[\(timestamp)] Event: \(type(of: event))")
+ }
+ }
+
+ private func formattedTime() -> String {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "HH:mm:ss"
+ return formatter.string(from: Date())
+ }
+}
+
// MARK: - mParticle Actions
func logSimpleEvent() {
@@ -718,6 +896,76 @@ func selectOverlayPlacementAutoClose() {
}
}
+func selectPlacementWithEventSubscription() {
+ let customAttributes: [String: String] = [
+ "email": "j.smit@example.com",
+ "firstname": "Jenny",
+ "lastname": "Smith",
+ "sandbox": "true",
+ "mobile": "(555)867-5309"
+ ]
+
+ let placementIdentifier = "RoktLayout"
+
+ // Subscribe to Rokt events for this placement
+ MParticle.sharedInstance().rokt.events(placementIdentifier) { event in
+ switch event {
+ case let initComplete as MPRoktEvent.MPRoktInitComplete:
+ print("Rokt Init Complete - Success: \(initComplete.success)")
+
+ case is MPRoktEvent.MPRoktShowLoadingIndicator:
+ print("Rokt: Show Loading Indicator")
+
+ case is MPRoktEvent.MPRoktHideLoadingIndicator:
+ print("Rokt: Hide Loading Indicator")
+
+ case let placementReady as MPRoktEvent.MPRoktPlacementReady:
+ print("Rokt Placement Ready - ID: \(placementReady.placementId ?? "unknown")")
+
+ case let placementInteractive as MPRoktEvent.MPRoktPlacementInteractive:
+ print("Rokt Placement Interactive - ID: \(placementInteractive.placementId ?? "unknown")")
+
+ case let offerEngagement as MPRoktEvent.MPRoktOfferEngagement:
+ print("Rokt Offer Engagement - ID: \(offerEngagement.placementId ?? "unknown")")
+
+ case let positiveEngagement as MPRoktEvent.MPRoktPositiveEngagement:
+ print("Rokt Positive Engagement - ID: \(positiveEngagement.placementId ?? "unknown")")
+
+ case let firstPositiveEngagement as MPRoktEvent.MPRoktFirstPositiveEngagement:
+ print("Rokt First Positive Engagement - ID: \(firstPositiveEngagement.placementId ?? "unknown")")
+
+ case let openUrl as MPRoktEvent.MPRoktOpenUrl:
+ print("Rokt Open URL - ID: \(openUrl.placementId ?? "unknown"), URL: \(openUrl.url)")
+
+ case let placementClosed as MPRoktEvent.MPRoktPlacementClosed:
+ print("Rokt Placement Closed - ID: \(placementClosed.placementId ?? "unknown")")
+
+ case let placementCompleted as MPRoktEvent.MPRoktPlacementCompleted:
+ print("Rokt Placement Completed - ID: \(placementCompleted.placementId ?? "unknown")")
+
+ case let placementFailure as MPRoktEvent.MPRoktPlacementFailure:
+ print("Rokt Placement Failure - ID: \(placementFailure.placementId ?? "unknown")")
+
+ case let cartItem as MPRoktEvent.MPRoktCartItemInstantPurchase:
+ print("Rokt Cart Item Instant Purchase:")
+ print(" - Placement ID: \(cartItem.placementId)")
+ print(" - Catalog Item ID: \(cartItem.catalogItemId)")
+ print(" - Cart Item ID: \(cartItem.cartItemId)")
+ print(" - Name: \(cartItem.name ?? "unknown")")
+ print(" - Currency: \(cartItem.currency)")
+ print(" - Unit Price: \(cartItem.unitPrice ?? 0)")
+ print(" - Total Price: \(cartItem.totalPrice ?? 0)")
+ print(" - Quantity: \(cartItem.quantity ?? 0)")
+
+ default:
+ print("Rokt: Unknown event type - \(type(of: event))")
+ }
+ }
+
+ // Select the placement (this will trigger events)
+ MParticle.sharedInstance().rokt.selectPlacements(placementIdentifier, attributes: customAttributes)
+}
+
#Preview {
ContentView()
}
diff --git a/UnitTests/Mocks/MPAppNotificationHandlerMock.swift b/UnitTests/Mocks/MPAppNotificationHandlerMock.swift
index 05d099f9d..73f6ad811 100644
--- a/UnitTests/Mocks/MPAppNotificationHandlerMock.swift
+++ b/UnitTests/Mocks/MPAppNotificationHandlerMock.swift
@@ -85,18 +85,20 @@ class MPAppNotificationHandlerMock: MPAppNotificationHandlerProtocol {
var openURLWithOptionsCalled = false
var openURLWithOptionsURLParam: URL?
var openURLWithOptionsOptionsParam: [String: Any]?
-
+
+ @objc(openURL:options:)
func open(_ url: URL, options: [String: Any]?) {
openURLWithOptionsCalled = true
openURLWithOptionsURLParam = url
openURLWithOptionsOptionsParam = options
}
-
+
var continueUserActivityCalled = false
var continueUserActivityUserActivityParam: NSUserActivity?
var continueUserActivityRestorationHandlerParam: (([UIUserActivityRestoring]?) -> Void)?
var continueUserActivityReturnValue: Bool = false
-
+
+ @objc(continueUserActivity:restorationHandler:)
func `continue`(_ userActivity: NSUserActivity,
restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
continueUserActivityCalled = true
diff --git a/UnitTests/Mocks/MPBackendControllerMock.swift b/UnitTests/Mocks/MPBackendControllerMock.swift
index a7128432e..b1479d8d8 100644
--- a/UnitTests/Mocks/MPBackendControllerMock.swift
+++ b/UnitTests/Mocks/MPBackendControllerMock.swift
@@ -69,7 +69,7 @@ class MPBackendControllerMock: NSObject, MPBackendControllerProtocol {
var prepareBatchesCalled = false
var prepareBatchesUploadSettingsParam: MPUploadSettings?
- func prepareBatches(forUpload uploadSettings: MPUploadSettings) {
+ func prepareBatches(forUpload uploadSettings: MPUploadSettings) -> Void {
prepareBatchesCalled = true
prepareBatchesUploadSettingsParam = uploadSettings
}
diff --git a/UnitTests/Mocks/MPUserDefaultsMock.swift b/UnitTests/Mocks/MPUserDefaultsMock.swift
index e1fcba0af..272fb5dc7 100644
--- a/UnitTests/Mocks/MPUserDefaultsMock.swift
+++ b/UnitTests/Mocks/MPUserDefaultsMock.swift
@@ -2,7 +2,7 @@ import XCTest
import mParticle_Apple_SDK
internal import mParticle_Apple_SDK_Swift
-class MPUserDefaultsMock: MPUserDefaultsProtocol {
+class MPUserDefaultsMock: mParticle_Apple_SDK_Swift.MPUserDefaultsProtocol {
var setMPObjectCalled = false
var setMPObjectValueParam: Any?
var setMPObjectKeyParam: String?
diff --git a/UnitTests/Mocks/SceneDelegateHandlerMock.swift b/UnitTests/Mocks/SceneDelegateHandlerMock.swift
index 06967287f..98de6b2c5 100644
--- a/UnitTests/Mocks/SceneDelegateHandlerMock.swift
+++ b/UnitTests/Mocks/SceneDelegateHandlerMock.swift
@@ -2,22 +2,23 @@ import XCTest
import mParticle_Apple_SDK
class OpenURLHandlerProtocolMock: NSObject, OpenURLHandlerProtocol {
-
var openURLWithOptionsCalled = false
var openURLWithOptionsURLParam: URL?
var openURLWithOptionsOptionsParam: [String: Any]?
-
+
+ @objc(openURL:options:)
func open(_ url: URL, options: [String: Any]?) {
openURLWithOptionsCalled = true
openURLWithOptionsURLParam = url
openURLWithOptionsOptionsParam = options
}
-
+
var continueUserActivityCalled = false
var continueUserActivityUserActivityParam: NSUserActivity?
var continueUserActivityRestorationHandlerParam: (([UIUserActivityRestoring]?) -> Void)?
var continueUserActivityReturnValue: Bool = false
-
+
+ @objc(continueUserActivity:restorationHandler:)
func `continue`(
_ userActivity: NSUserActivity,
restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void
diff --git a/UnitTests/ObjCTests/MPBackendControllerTests.m b/UnitTests/ObjCTests/MPBackendControllerTests.m
index 38cf89ec1..d28040f2d 100644
--- a/UnitTests/ObjCTests/MPBackendControllerTests.m
+++ b/UnitTests/ObjCTests/MPBackendControllerTests.m
@@ -78,8 +78,6 @@ - (void)handleApplicationDidBecomeActive:(NSNotification *)notification;
- (void)logRemoteNotificationWithNotificationController:(MPNotificationController_PRIVATE *const)notificationController;
- (void)parseConfigResponse:(NSDictionary *)configurationDictionary;
- (void)parseResponseHeader:(NSDictionary *)responseDictionary session:(MPSession *)session;
-- (NSNumber *)previousSessionSuccessfullyClosed;
-- (void)setPreviousSessionSuccessfullyClosed:(NSNumber *)previousSessionSuccessfullyClosed;
- (void)processOpenSessionsEndingCurrent:(BOOL)endCurrentSession completionHandler:(dispatch_block_t)completionHandler;
- (void)resetUserIdentitiesFirstTimeUseFlag;
- (void)saveMessage:(MPMessage *)message updateSession:(BOOL)updateSession;
@@ -126,10 +124,10 @@ @implementation MPBackendControllerTests
- (void)setUp {
[super setUp];
-
+
[MPPersistenceController_PRIVATE setMpid:@1];
[MParticle sharedInstance].persistenceController = [[MPPersistenceController_PRIVATE alloc] init];
-
+
// Must read messageQueue AFTER [MParticle sharedInstance] triggers singleton
// recreation, otherwise we get the old executor's queue.
messageQueue = [MParticle messageQueue];
diff --git a/UnitTests/SwiftTests/MParticle/MParticleSceneDelegateTests.swift b/UnitTests/SwiftTests/MParticle/MParticleSceneDelegateTests.swift
index 8ad7b6667..a73338964 100644
--- a/UnitTests/SwiftTests/MParticle/MParticleSceneDelegateTests.swift
+++ b/UnitTests/SwiftTests/MParticle/MParticleSceneDelegateTests.swift
@@ -3,27 +3,56 @@ import XCTest
internal import mParticle_Apple_SDK_Swift
final class MParticleSceneDelegateTests: XCTestCase {
-
+
// MARK: - Properties
var mparticle: MParticle!
var sceneMock: OpenURLHandlerProtocolMock!
- var testURL: URL!
var testUserActivity: NSUserActivity!
-
+
override func setUp() {
super.setUp()
mparticle = MParticle()
- testURL = URL(string: "myapp://test/path?param=value")!
testUserActivity = NSUserActivity(activityType: "com.test.activity")
testUserActivity.title = "Test Activity"
testUserActivity.userInfo = ["key": "value"]
-
- // The implementation calls [MParticle sharedInstance], so we need to set the mock on the shared instance
+
sceneMock = OpenURLHandlerProtocolMock()
let sceneHandler = SceneDelegateHandler(appNotificationHandler: sceneMock)
mparticle.sceneDelegateHandler = sceneHandler
}
+
+ // MARK: - Protocol Selector Tests
+
+ func test_openURLHandlerProtocol_respondsToOpenURLSelector() {
+ // This test verifies that the protocol method maps to the correct ObjC selector
+ let handler: OpenURLHandlerProtocol = sceneMock
+ let selector = NSSelectorFromString("openURL:options:")
+ XCTAssertTrue((handler as AnyObject).responds(to: selector),
+ "OpenURLHandlerProtocol should respond to openURL:options: selector")
+ }
+
+ func test_openURLHandlerProtocol_respondsToContiuneUserActivitySelector() {
+ // This test verifies that the protocol method maps to the correct ObjC selector
+ let handler: OpenURLHandlerProtocol = sceneMock
+ let selector = NSSelectorFromString("continueUserActivity:restorationHandler:")
+ XCTAssertTrue((handler as AnyObject).responds(to: selector),
+ "OpenURLHandlerProtocol should respond to continueUserActivity:restorationHandler: selector")
+ }
+
+ func test_mpAppNotificationHandler_respondsToOpenURLHandlerProtocolSelectors() {
+ // This test verifies that MPAppNotificationHandler responds to the selectors
+ // expected by OpenURLHandlerProtocol
+ let handler = MPAppNotificationHandler()
+
+ let openURLSelector = NSSelectorFromString("openURL:options:")
+ XCTAssertTrue(handler.responds(to: openURLSelector),
+ "MPAppNotificationHandler must respond to openURL:options: to conform to OpenURLHandlerProtocol")
+
+ let continueActivitySelector = NSSelectorFromString("continueUserActivity:restorationHandler:")
+ XCTAssertTrue(handler.responds(to: continueActivitySelector),
+ "MPAppNotificationHandler must respond to continueUserActivity:restorationHandler: to conform to OpenURLHandlerProtocol")
+ }
// MARK: - handleUserActivity Tests
@@ -54,14 +83,14 @@ final class MParticleSceneDelegateTests: XCTestCase {
func test_handleUserActivity_restorationHandlerIsEmpty() {
// Act
mparticle.handleUserActivity(testUserActivity)
-
+
// Assert
XCTAssertTrue(sceneMock.continueUserActivityCalled)
-
+
// Verify the restoration handler is provided and safe to call
let restorationHandler = sceneMock.continueUserActivityRestorationHandlerParam
XCTAssertNotNil(restorationHandler)
-
+
// Test that calling the restoration handler doesn't crash
XCTAssertNoThrow(restorationHandler?(nil))
XCTAssertNoThrow(restorationHandler?([]))
diff --git a/mParticle-Apple-SDK.podspec b/mParticle-Apple-SDK.podspec
index d68827670..75e65c545 100644
--- a/mParticle-Apple-SDK.podspec
+++ b/mParticle-Apple-SDK.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "mParticle-Apple-SDK"
- s.version = "8.43.1"
+ s.version = "8.44.0"
s.summary = "mParticle Apple SDK."
s.description = <<-DESC
diff --git a/mParticle-Apple-SDK/Include/MPBackendController.h b/mParticle-Apple-SDK/Include/MPBackendController.h
index 1b7042ca0..6c449a43c 100644
--- a/mParticle-Apple-SDK/Include/MPBackendController.h
+++ b/mParticle-Apple-SDK/Include/MPBackendController.h
@@ -56,7 +56,7 @@ extern const NSInteger kInvalidKey;
- (void)setOptOut:(BOOL)optOutStatus completionHandler:(void (^ _Nonnull)(BOOL optOut, MPExecStatus execStatus))completionHandler;
- (nonnull NSMutableDictionary *)userAttributesForUserId:(nonnull NSNumber *)userId;
- (void)startWithKey:(nonnull NSString *)apiKey secret:(nonnull NSString *)secret networkOptions:(nullable MPNetworkOptions *)networkOptions firstRun:(BOOL)firstRun installationType:(MPInstallationType)installationType startKitsAsync:(BOOL)startKitsAsync consentState:(MPConsentState *_Nullable)consentState completionHandler:(dispatch_block_t _Nonnull)completionHandler;
-- (void)prepareBatchesForUpload:(nonnull MPUploadSettings *)uploadSettings;
+- (void)prepareBatchesForUpload:(nonnull MPUploadSettings *)uploadSettings NS_SWIFT_NAME(prepareBatches(forUpload:));
- (MParticleSession* _Nullable)tempSession;
- (void)endSession;
- (void)beginTimedEvent:(nonnull MPEvent *)event completionHandler:(void (^ _Nonnull)(MPEvent * _Nonnull event, MPExecStatus execStatus))completionHandler;
@@ -134,7 +134,7 @@ extern const NSInteger kInvalidKey;
- (MPExecStatus)waitForKitsAndUploadWithCompletionHandler:(void (^ _Nullable)(void))completionHandler;
- (nonnull NSMutableDictionary *)userAttributesForUserId:(nonnull NSNumber *)userId;
- (nonnull NSMutableArray *> *)userIdentitiesForUserId:(nonnull NSNumber *)userId;
-- (void)prepareBatchesForUpload:(nonnull MPUploadSettings *)uploadSettings;
+- (void)prepareBatchesForUpload:(nonnull MPUploadSettings *)uploadSettings NS_SWIFT_NAME(prepareBatches(forUpload:));
#if TARGET_OS_IOS == 1
- (void)handleDeviceTokenNotification:(nonnull NSNotification *)notification;
diff --git a/mParticle-Apple-SDK/Include/SceneDelegateHandler.h b/mParticle-Apple-SDK/Include/SceneDelegateHandler.h
index 025f3563d..704364a05 100644
--- a/mParticle-Apple-SDK/Include/SceneDelegateHandler.h
+++ b/mParticle-Apple-SDK/Include/SceneDelegateHandler.h
@@ -5,7 +5,7 @@
NS_ASSUME_NONNULL_BEGIN
@protocol OpenURLHandlerProtocol
-- (void)open:(NSURL *)url options:(nullable NSDictionary *)options;
+- (void)openURL:(NSURL *)url options:(nullable NSDictionary *)options;
- (BOOL)continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^_Nonnull)(NSArray> * _Nullable))restorationHandler;
@end
@@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)handleUserActivity:(NSUserActivity *)userActivity;
#if TARGET_OS_IOS == 1
-- (void)handleWithUrlContext:(UIOpenURLContext *)urlContext API_AVAILABLE(ios(13.0));
+- (void)handleURLContext:(UIOpenURLContext *)urlContext API_AVAILABLE(ios(13.0));
#endif
@end
diff --git a/mParticle-Apple-SDK/MPBackendController.m b/mParticle-Apple-SDK/MPBackendController.m
index 6d0c7ef6c..724415999 100644
--- a/mParticle-Apple-SDK/MPBackendController.m
+++ b/mParticle-Apple-SDK/MPBackendController.m
@@ -320,50 +320,6 @@ - (void)logUserIdentityChange:(MPUserIdentityChangePRIVATE *)userIdentityChange
[self saveMessage:message updateSession:YES];
}
-- (NSNumber *)previousSessionSuccessfullyClosed {
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSString *stateMachineDirectoryPath = STATE_MACHINE_DIRECTORY_PATH;
- NSString *previousSessionStateFile = [stateMachineDirectoryPath stringByAppendingPathComponent:kMPPreviousSessionStateFileName];
- NSNumber *previousSessionSuccessfullyClosed = nil;
- if ([fileManager fileExistsAtPath:previousSessionStateFile]) {
- NSDictionary *previousSessionStateDictionary = [NSDictionary dictionaryWithContentsOfFile:previousSessionStateFile];
- previousSessionSuccessfullyClosed = previousSessionStateDictionary[kMPASTPreviousSessionSuccessfullyClosedKey];
- }
-
- if (previousSessionSuccessfullyClosed == nil) {
- previousSessionSuccessfullyClosed = @YES;
- }
-
- return previousSessionSuccessfullyClosed;
-}
-
-- (void)setPreviousSessionSuccessfullyClosed:(NSNumber *)previousSessionSuccessfullyClosed {
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSString *stateMachineDirectoryPath = STATE_MACHINE_DIRECTORY_PATH;
- NSString *previousSessionStateFile = [stateMachineDirectoryPath stringByAppendingPathComponent:kMPPreviousSessionStateFileName];
- NSDictionary *previousSessionStateDictionary = @{kMPASTPreviousSessionSuccessfullyClosedKey:previousSessionSuccessfullyClosed};
-
- @try {
- if (![fileManager fileExistsAtPath:stateMachineDirectoryPath]) {
- NSError *dirError = nil;
- [fileManager createDirectoryAtPath:stateMachineDirectoryPath withIntermediateDirectories:YES attributes:nil error:&dirError];
- if (dirError) {
- MPILogError(@"Failed to create state machine directory: %@", dirError);
- return;
- }
- } else if ([fileManager fileExistsAtPath:previousSessionStateFile]) {
- [fileManager removeItemAtPath:previousSessionStateFile error:nil];
- }
-
- BOOL success = [previousSessionStateDictionary writeToFile:previousSessionStateFile atomically:YES];
- if (!success) {
- MPILogError(@"Failed to write previous session state to file");
- }
- } @catch (NSException *exception) {
- MPILogError(@"Exception writing previous session state: %@", exception);
- }
-}
-
- (void)processDidFinishLaunching:(NSNotification *)notification {
NSString *astType = kMPASTInitKey;
NSMutableDictionary *messageInfo = [[NSMutableDictionary alloc] initWithCapacity:3];
@@ -380,8 +336,6 @@ - (void)processDidFinishLaunching:(NSNotification *)notification {
isInstallOrUpgrade = YES;
}
- messageInfo[kMPASTPreviousSessionSuccessfullyClosedKey] = [self previousSessionSuccessfullyClosed];
-
NSDictionary *userInfo = [notification userInfo];
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
@@ -2007,7 +1961,6 @@ - (void)handleApplicationDidEnterBackground:(NSNotification *)notification {
self.timeAppWentToBackgroundInCurrentSession = currentTime;
self.timeOfLastEventInBackground = currentTime;
- [self setPreviousSessionSuccessfullyClosed:@YES];
[self cleanUp];
MPMessageBuilder *messageBuilder = [[MPMessageBuilder alloc] initWithMessageType:MPMessageTypeAppStateTransition
diff --git a/mParticle-Apple-SDK/MPIConstants.h b/mParticle-Apple-SDK/MPIConstants.h
index 891bbc562..22de8a0cd 100644
--- a/mParticle-Apple-SDK/MPIConstants.h
+++ b/mParticle-Apple-SDK/MPIConstants.h
@@ -206,7 +206,6 @@ extern NSString * _Nonnull const kMPUploadIntervalKey;
extern NSString * _Nonnull const kMPPreviousSessionLengthKey;
extern NSString * _Nonnull const kMPLifeTimeValueKey;
extern NSString * _Nonnull const kMPIncreasedLifeTimeValueKey;
-extern NSString * _Nonnull const kMPPreviousSessionStateFileName;
extern NSString * _Nonnull const kMPHTTPMethodPost;
extern NSString * _Nonnull const kMPHTTPMethodGet;
extern NSString * _Nonnull const kMPPreviousSessionIdKey;
@@ -384,7 +383,6 @@ extern NSString * _Nonnull const kMPASTBackgroundKey;
extern NSString * _Nonnull const kMPASTForegroundKey;
extern NSString * _Nonnull const kMPASTIsFirstRunKey;
extern NSString * _Nonnull const kMPASTIsUpgradeKey;
-extern NSString * _Nonnull const kMPASTPreviousSessionSuccessfullyClosedKey;
// Network performance
extern NSString * _Nonnull const kMPNetworkPerformanceMeasurementNotification;
diff --git a/mParticle-Apple-SDK/MPIConstants.m b/mParticle-Apple-SDK/MPIConstants.m
index cc5b7c96b..dbc104621 100644
--- a/mParticle-Apple-SDK/MPIConstants.m
+++ b/mParticle-Apple-SDK/MPIConstants.m
@@ -1,7 +1,7 @@
#import "MPIConstants.h"
// mParticle SDK Version
-NSString *const kMParticleSDKVersion = @"8.43.1";
+NSString *const kMParticleSDKVersion = @"8.44.0";
// Message Type (dt)
NSString *const kMPMessageTypeKey = @"dt";
@@ -139,7 +139,6 @@
NSString *const kMPPreviousSessionLengthKey = @"psl";
NSString *const kMPLifeTimeValueKey = @"ltv";
NSString *const kMPIncreasedLifeTimeValueKey = @"iltv";
-NSString *const kMPPreviousSessionStateFileName = @"PreviousSessionState.dic";
NSString *const kMPHTTPMethodPost = @"POST";
NSString *const kMPHTTPMethodGet = @"GET";
NSString *const kMPPreviousSessionIdKey = @"pid";
@@ -309,7 +308,6 @@
NSString *const kMPASTForegroundKey = @"app_fore";
NSString *const kMPASTIsFirstRunKey = @"ifr";
NSString *const kMPASTIsUpgradeKey = @"iu";
-NSString *const kMPASTPreviousSessionSuccessfullyClosedKey = @"sc";
// Network performance
NSString *const kMPNetworkPerformanceMeasurementNotification = @"MPNetworkPerformanceMeasurement";
diff --git a/mParticle-Apple-SDK/SceneDelegateHandler.m b/mParticle-Apple-SDK/SceneDelegateHandler.m
index 9c491be70..692cc73bf 100644
--- a/mParticle-Apple-SDK/SceneDelegateHandler.m
+++ b/mParticle-Apple-SDK/SceneDelegateHandler.m
@@ -17,7 +17,7 @@ - (instancetype)initWithAppNotificationHandler:(id)appNo
}
#if TARGET_OS_IOS
-- (void)handleWithUrlContext:(UIOpenURLContext *)urlContext API_AVAILABLE(ios(13.0)) {
+- (void)handleURLContext:(UIOpenURLContext *)urlContext API_AVAILABLE(ios(13.0)) {
MPILogDebug(@"Opening URLContext URL: %@", urlContext.URL);
MPILogDebug(@"Source: %@", urlContext.options.sourceApplication ?: @"unknown");
@@ -34,7 +34,7 @@ - (void)handleWithUrlContext:(UIOpenURLContext *)urlContext API_AVAILABLE(ios(13
options[@"UIApplicationOpenURLOptionsSourceApplicationKey"] = urlContext.options.sourceApplication;
}
- [self.appNotificationHandler open:urlContext.URL options:options];
+ [self.appNotificationHandler openURL:urlContext.URL options:options];
}
#endif
diff --git a/mParticle-Apple-SDK/mParticle.m b/mParticle-Apple-SDK/mParticle.m
index 2bd2e87d0..724488382 100644
--- a/mParticle-Apple-SDK/mParticle.m
+++ b/mParticle-Apple-SDK/mParticle.m
@@ -658,7 +658,7 @@ - (void)handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNoti
}
- (void)handleURLContext:(UIOpenURLContext *)urlContext API_AVAILABLE(ios(13.0)) {
- [self.sceneDelegateHandler handleWithUrlContext:urlContext];
+ [self.sceneDelegateHandler handleURLContext:urlContext];
}
#endif
diff --git a/mParticle_Apple_SDK.json b/mParticle_Apple_SDK.json
index 485f9be55..9bb995bc5 100644
--- a/mParticle_Apple_SDK.json
+++ b/mParticle_Apple_SDK.json
@@ -126,5 +126,6 @@
"8.42.0": "https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.42.0/mParticle_Apple_SDK.framework.zip?alt=https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.42.0/mParticle_Apple_SDK.xcframework.zip",
"8.42.1": "https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.42.1/mParticle_Apple_SDK.framework.zip?alt=https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.42.1/mParticle_Apple_SDK.xcframework.zip",
"8.42.2": "https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.42.2/mParticle_Apple_SDK.framework.zip?alt=https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.42.2/mParticle_Apple_SDK.xcframework.zip",
- "8.43.1": "https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.43.1/mParticle_Apple_SDK.framework.zip?alt=https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.43.1/mParticle_Apple_SDK.xcframework.zip"
+ "8.43.1": "https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.43.1/mParticle_Apple_SDK.framework.zip?alt=https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.43.1/mParticle_Apple_SDK.xcframework.zip",
+ "8.44.0": "https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.44.0/mParticle_Apple_SDK.framework.zip?alt=https://github.com/mParticle/mparticle-apple-sdk/releases/download/v8.44.0/mParticle_Apple_SDK.xcframework.zip"
}