diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index c2e5c88..d50c922 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -13,6 +13,11 @@ permissions: checks: write id-token: write +env: + XCODE_VERSION: "16.4" + DEVICE: "iPhone 16 Pro" + VERSION: ">=18.0" + jobs: pr-branch-check-name: name: "Check PR for semantic branch name" @@ -23,20 +28,30 @@ jobs: test: name: Test + timeout-minutes: 15 runs-on: macos-15 steps: - name: Checkout - uses: actions/checkout@v5 - - name: Set up Xcode 16 + uses: actions/checkout@v6 + + - name: Set up Xcode uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 16.3.0 - - name: Run Tests - run: > - set -o pipefail && - xcodebuild test -project mParticle-Rokt.xcodeproj -scheme mParticle_RoktTests -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' - | xcbeautify --renderer github-actions - shell: bash + xcode-version: ${{ env.XCODE_VERSION }} + + - name: Setup specified simulator + uses: futureware-tech/simulator-action@v4 + id: simulator + with: + model: ${{ env.DEVICE }} + os: iOS + os_version: ${{ env.VERSION }} + erase_before_boot: true + wait_for_boot: true + shutdown_after_job: true + + - name: Run unit tests + run: xcodebuild -project mParticle-Rokt.xcodeproj -scheme mParticle_RoktTests -destination 'id=${{ steps.simulator.outputs.UDID }}' test pr-notify: if: > diff --git a/mParticle-Rokt-Swift/MPRoktLayout.swift b/mParticle-Rokt-Swift/MPRoktLayout.swift index c3ed756..9c4bf42 100644 --- a/mParticle-Rokt-Swift/MPRoktLayout.swift +++ b/mParticle-Rokt-Swift/MPRoktLayout.swift @@ -33,6 +33,9 @@ public class MPRoktLayout { confirmUser(attributes: attributes) { identifyCalled in let preparedAttributes = MPKitRokt.prepareAttributes(attributes, filteredUser: Optional.none, performMapping: true) + // Log custom event for selectPlacements call + MPKitRokt.logSelectPlacementEvent(preparedAttributes) + MPRoktLayout.mpLog("Initializing RoktLayout with arguments sdkTriggered:\(sdkTriggered.wrappedValue), viewName: \(viewName ?? "nil"), locationName:\(locationName), attributes:\(preparedAttributes)") self.roktLayout = RoktLayout.init( sdkTriggered: sdkTriggered, diff --git a/mParticle-Rokt/MPKitRokt.h b/mParticle-Rokt/MPKitRokt.h index a646420..b4a3f30 100644 --- a/mParticle-Rokt/MPKitRokt.h +++ b/mParticle-Rokt/MPKitRokt.h @@ -18,5 +18,6 @@ + (NSDictionary * _Nonnull)prepareAttributes:(NSDictionary * _Nonnull)attributes filteredUser:(FilteredMParticleUser * _Nullable)filteredUser performMapping:(BOOL)performMapping; + (NSNumber * _Nullable)getRoktHashedEmailUserIdentityType; ++ (void)logSelectPlacementEvent:(NSDictionary * _Nonnull)attributes; @end diff --git a/mParticle-Rokt/MPKitRokt.m b/mParticle-Rokt/MPKitRokt.m index 6ea51e8..8d5597e 100644 --- a/mParticle-Rokt/MPKitRokt.m +++ b/mParticle-Rokt/MPKitRokt.m @@ -8,6 +8,7 @@ NSString * const kMPPlacementAttributesMapping = @"placementAttributesMapping"; NSString * const kMPHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; NSString * const kMPRoktEmbeddedViewClassName = @"MPRoktEmbeddedView"; +NSString * const kMPEventNameSelectPlacements = @"selectPlacements"; NSInteger const kMPRoktKitCode = 181; static __weak MPKitRokt *roktKit = nil; @@ -104,6 +105,9 @@ - (MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier [MPKitRokt MPLog:[NSString stringWithFormat:@"Rokt Kit recieved `executeWithIdentifier` method with the following arguments: \n identifier: %@ \n attributes: %@ \n embeddedViews: %@ \n config: %@ \n callbacks: %@ \n filteredUser identities: %@", identifier, attributes, embeddedViews, mpRoktConfig, callbacks, filteredUser.userIdentities]]; NSDictionary *finalAtt = [MPKitRokt prepareAttributes:attributes filteredUser:filteredUser performMapping:NO]; + // Log custom event for selectPlacements call + [MPKitRokt logSelectPlacementEvent:finalAtt]; + //Convert MPRoktConfig to RoktConfig RoktConfig *roktConfig = [MPKitRokt convertMPRoktConfig:mpRoktConfig]; NSDictionary *confirmedViews = [self confirmEmbeddedViews:embeddedViews]; @@ -786,6 +790,13 @@ + (void)MPLog:(NSString *)string { } } ++ (void)logSelectPlacementEvent:(NSDictionary * _Nonnull)attributes { + MPEvent *event = [[MPEvent alloc] initWithName:kMPEventNameSelectPlacements type:MPEventTypeOther]; + event.customAttributes = attributes; + [[MParticle sharedInstance] logEvent:event]; + [MPKitRokt MPLog:[NSString stringWithFormat:@"Logged selectplacements custom event with attributes: %@", attributes]]; +} + + (MPRoktEvent * _Nullable)mapEvent:(RoktEvent *)event { if (!event) { diff --git a/mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift b/mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift index b323e0b..70ae639 100644 --- a/mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift +++ b/mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift @@ -252,6 +252,39 @@ struct mParticle_Rokt_SwiftTests { #expect(layout.roktLayout != nil, "Layout should handle state changes") } + // MARK: - SelectPlacements Custom Event Tests + + @available(iOS 15, *) + @Test func testPrepareAttributesLogsAttributesEvent() { + // Given + let attributes: [String: String] = ["attr1": "val1"] + + // When + let preparedAttributes = MPKitRokt.prepareAttributes( + attributes, + filteredUser: nil, + performMapping: false + ) + + MPKitRokt.logSelectPlacementEvent(preparedAttributes) + + // Then + #expect(preparedAttributes["sandbox"] != nil, "Sandbox attribute should be present") + #expect(preparedAttributes.count >= 1, "Prepared attributes should contain at least the sandbox attribute") + } + + @available(iOS 15, *) + @Test func testLogSelectPlacementEventHandlesNilMParticleInstance() { + // Given + let attributes: [String: String] = ["key1": "value1"] + + // When + MPKitRokt.logSelectPlacementEvent(attributes) + + // Then + #expect(true, "logSelectPlacementEvent should handle MParticle instance state gracefully") + } + // MARK: - Integration Tests @MainActor @available(iOS 15, *)