Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 68 additions & 14 deletions mParticle-Rokt-Swift/MPRoktLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import mParticle_Apple_SDK
import mParticle_Rokt

@available(iOS 15, *)
public struct MPRoktLayout: View {
private var roktLayout: RoktLayout
public class MPRoktLayout {
public var roktLayout: RoktLayout? = nil
let mparticle = MParticle.sharedInstance()

public init(
sdkTriggered: Binding<Bool>,
Expand All @@ -28,19 +29,72 @@ public struct MPRoktLayout: View {
config: RoktConfig? = nil,
onEvent: ((RoktEvent) -> Void)? = nil
) {
let preparedAttributes = MPKitRokt.prepareAttributes(attributes, filteredUser: Optional<FilteredMParticleUser>.none, performMapping: true)
confirmUser(attributes: attributes) {
let preparedAttributes = MPKitRokt.prepareAttributes(attributes, filteredUser: Optional<FilteredMParticleUser>.none, performMapping: true)
Comment thread
BrandonStalnaker marked this conversation as resolved.

self.roktLayout = RoktLayout.init(
sdkTriggered: sdkTriggered,
viewName: viewName,
locationName: locationName,
attributes: preparedAttributes,
config: config,
onEvent: onEvent
)
self.roktLayout = RoktLayout.init(
sdkTriggered: sdkTriggered,
viewName: viewName,
locationName: locationName,
attributes: preparedAttributes,
config: config,
onEvent: onEvent
)
}
}

public var body: some View {
return self.roktLayout.body

func confirmUser(
attributes: [String: String]?,
completion: @escaping () -> Void
) {
guard let user = mparticle.identity.currentUser else {
completion()
return
}
let email = attributes?["email"]
let hashedEmail = attributes?["emailsha256"]

let userEmailIdentity = user.identities[NSNumber(value: MPIdentity.email.rawValue)]
let userHashedEmailIdentity = user.identities[NSNumber(value: MPIdentity.other.rawValue)]

let emailMismatch = email != nil && email != userEmailIdentity
let hashedEmailMismatch = hashedEmail != nil && hashedEmail != userHashedEmailIdentity

if emailMismatch || hashedEmailMismatch {
// If there is an existing email or hashed email but it doesn't match what was passed in, warn the customer
if emailMismatch {
print("The existing email on the user (\(userEmailIdentity ?? "nil")) does not match the email passed in to `selectPlacements:` (\(email ?? "nil")). Please remember to sync the email identity to mParticle as soon as you receive it. We will now identify the user before creating the layout")
}
if hashedEmailMismatch {
print("The existing hashed email on the user (\(userHashedEmailIdentity ?? "nil")) does not match the email passed in to `selectPlacements:` (\(hashedEmail ?? "nil")). Please remember to sync the email identity to mParticle as soon as you receive it. We will now identify the user before creating the layout")
}

syncIdentities(user: user, email: email, hashedEmail: hashedEmail, completion: completion)
} else {
completion()
}
}

func syncIdentities(
user: MParticleUser,
email: String?,
hashedEmail: String?,
completion: @escaping () -> Void
) {
let identityRequest = MPIdentityApiRequest(user: user)
identityRequest.setIdentity(email, identityType: .email)
identityRequest.setIdentity(hashedEmail, identityType: .other)

mparticle.identity.identify(identityRequest) {apiResult, error in
if let error = error {
print("Failed to sync email from selectPlacement to user: \(error)")
completion()
} else {
if let identities = apiResult?.user.identities {
print("Updated user identity based off selectPlacement's attributes: \(identities)")
}
completion()
}
}
}
}
44 changes: 13 additions & 31 deletions mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
)

// Then
#expect(layout.body != nil, "Layout body should not be nil")
#expect(layout.roktLayout != nil, "Layout.roktLayout should not be nil")
}

@MainActor @available(iOS 15, *)
Expand All @@ -58,7 +58,7 @@
)

// Then
#expect(layout.body != nil, "Layout body should not be nil")
#expect(layout.roktLayout != nil, "Layout.roktLayout should not be nil")
}

@MainActor @available(iOS 15, *)
Expand All @@ -76,7 +76,7 @@
)

// Then
#expect(layout.body != nil, "Layout should handle empty attributes")
#expect(layout.roktLayout != nil, "Layout should handle empty attributes")
}

@MainActor @available(iOS 15, *)
Expand All @@ -94,7 +94,7 @@
)

// Then
#expect(layout.body != nil, "Layout should handle sandbox attribute")
#expect(layout.roktLayout != nil, "Layout should handle sandbox attribute")
}

// MARK: - Attribute Preparation Tests
Expand All @@ -112,7 +112,7 @@
)

// Then
#expect(preparedAttributes != nil, "Prepared attributes should not be nil")

Check warning on line 115 in mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift

View workflow job for this annotation

GitHub Actions / Test

comparing non-optional value of type '[String : String]' to 'nil' always returns true
#expect(preparedAttributes.count >= attributes.count, "Prepared attributes should contain at least the original attributes")
}

Expand All @@ -129,7 +129,7 @@
)

// Then
#expect(preparedAttributes != nil, "Prepared attributes should not be nil")

Check warning on line 132 in mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift

View workflow job for this annotation

GitHub Actions / Test

comparing non-optional value of type '[String : String]' to 'nil' always returns true
#expect(preparedAttributes["sandbox"] != nil, "Sandbox attribute should be added automatically")
}

Expand Down Expand Up @@ -183,25 +183,7 @@
)

// Then
#expect(layout is any View, "MPRoktLayout should conform to SwiftUI View protocol")
}

@MainActor @available(iOS 15, *)
@Test func testMPRoktLayoutBodyProperty() {
// Given
let sdkTriggered = Binding.constant(false)
let attributes: [String: String] = ["test": "value"]

// When
let layout = MPRoktLayout(
sdkTriggered: sdkTriggered,
locationName: "test",
attributes: attributes
)

// Then
let body = layout.body
#expect(body != nil, "Layout body should be accessible")
#expect(layout.roktLayout is any View, "MPRoktLayout should conform to SwiftUI View protocol")

Check warning on line 186 in mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift

View workflow job for this annotation

GitHub Actions / Test

checking a value with optional type 'RoktLayout?' against type 'any View' succeeds whenever the value is non-nil; did you mean to use '!= nil'?
}

// MARK: - Parameter Validation Tests
Expand All @@ -212,16 +194,16 @@
let sdkTriggered = Binding.constant(false)
let longLocationName = String(repeating: "a", count: 1000)
let attributes: [String: String] = ["test": "value"]

// When
let layout = MPRoktLayout(
sdkTriggered: sdkTriggered,
locationName: longLocationName,
attributes: attributes
)

// Then
#expect(layout.body != nil, "Layout should handle long location names")
#expect(layout.roktLayout != nil, "Layout should handle long location names")
}

@MainActor @available(iOS 15, *)
Expand All @@ -242,7 +224,7 @@
)

// Then
#expect(layout.body != nil, "Layout should handle special characters and unicode")
#expect(layout.roktLayout != nil, "Layout should handle special characters and unicode")
}

// MARK: - State Management Tests
Expand All @@ -261,13 +243,13 @@
)

// Then
#expect(layout.body != nil, "Layout should be created with initial state")
#expect(layout.roktLayout != nil, "Layout should be created with initial state")

// When state changes
sdkTriggered.wrappedValue = true

// Then
#expect(layout.body != nil, "Layout should handle state changes")
#expect(layout.roktLayout != nil, "Layout should handle state changes")
}

// MARK: - Integration Tests
Expand All @@ -291,7 +273,7 @@
)

// Then
#expect(layout.body != nil, "Layout should properly integrate attribute processing")
#expect(layout.roktLayout == nil, "Layout should attempt to identify user attributes and fail")
}

@MainActor @available(iOS 15, *)
Expand Down Expand Up @@ -325,6 +307,6 @@
)

// Then
#expect(layout.body != nil, "Layout should handle complex configurations")
#expect(layout.roktLayout != nil, "Layout should handle complex configurations")
}
}
Loading