Skip to content

Commit

Permalink
fix(Analytics): Making PinpointEndpointProfile thread safe.
Browse files Browse the repository at this point in the history
  • Loading branch information
ruisebas committed Jan 4, 2024
1 parent a0ee257 commit 0352f8d
Showing 1 changed file with 102 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import AWSPinpoint
import Foundation

@_spi(InternalAWSPinpoint)
public class PinpointEndpointProfile: Codable {
public final class PinpointEndpointProfile {
typealias DeviceToken = String

var applicationId: String
Expand All @@ -22,8 +22,12 @@ public class PinpointEndpointProfile: Codable {
var location: PinpointClientTypes.EndpointLocation
var demographic: PinpointClientTypes.EndpointDemographic
private(set) var user: PinpointClientTypes.EndpointUser
private(set) var attributes: [String: [String]] = [:]
private(set) var metrics: [String: Double] = [:]
private(set) var attributes: [String: [String]]
private(set) var metrics: [String: Double]
private let profileQueue = DispatchQueue(
label: Constants.queue,
attributes: .concurrent
)

init(applicationId: String,
endpointId: String,
Expand All @@ -33,7 +37,10 @@ public class PinpointEndpointProfile: Codable {
isOptOut: Bool = false,
location: PinpointClientTypes.EndpointLocation = .init(),
demographic: PinpointClientTypes.EndpointDemographic = .init(),
user: PinpointClientTypes.EndpointUser = .init()) {
user: PinpointClientTypes.EndpointUser = .init(),
attributes: [String: [String]] = [:],
metrics: [String: Double] = [:]
) {
self.applicationId = applicationId
self.endpointId = endpointId
self.deviceToken = deviceToken
Expand All @@ -43,40 +50,48 @@ public class PinpointEndpointProfile: Codable {
self.location = location
self.demographic = demographic
self.user = user
self.attributes = attributes
self.metrics = metrics
}

public func addUserId(_ userId: String) {
user.userId = userId
profileQueue.sync(flags: .barrier) {
user.userId = userId
}
}

public func addUserProfile(_ userProfile: UserProfile) {
if let email = userProfile.email {
setCustomProperty(email, forKey: Constants.AttributeKeys.email)
}

if let name = userProfile.name {
setCustomProperty(name, forKey: Constants.AttributeKeys.name)
}

if let plan = userProfile.plan {
setCustomProperty(plan, forKey: Constants.AttributeKeys.plan)
}

addCustomProperties(userProfile.customProperties)
if let pinpointUser = userProfile as? PinpointUserProfile {
addUserAttributes(pinpointUser.userAttributes)
if let optedOutOfMessages = pinpointUser.optedOutOfMessages {
isOptOut = optedOutOfMessages
profileQueue.sync(flags: .barrier) {
if let email = userProfile.email {
setCustomProperty(email, forKey: Constants.AttributeKeys.email)
}

if let name = userProfile.name {
setCustomProperty(name, forKey: Constants.AttributeKeys.name)
}

if let plan = userProfile.plan {
setCustomProperty(plan, forKey: Constants.AttributeKeys.plan)
}

addCustomProperties(userProfile.customProperties)
if let pinpointUser = userProfile as? PinpointUserProfile {
addUserAttributes(pinpointUser.userAttributes)
if let optedOutOfMessages = pinpointUser.optedOutOfMessages {
isOptOut = optedOutOfMessages
}
}

if let userLocation = userProfile.location {
location.update(with: userLocation)
}
}

if let userLocation = userProfile.location {
location.update(with: userLocation)
}
}

public func setAPNsToken(_ apnsToken: Data) {
deviceToken = apnsToken.asHexString()
profileQueue.sync(flags: .barrier) {
deviceToken = apnsToken.asHexString()
}
}

private func addCustomProperties(_ properties: [String: UserProfilePropertyValue]?) {
Expand Down Expand Up @@ -125,5 +140,65 @@ extension PinpointEndpointProfile {
static let name = "name"
static let plan = "plan"
}

static let queue = "com.amazonaws.Amplify.PinpointEndpointProfileQueue"
}
}

extension PinpointEndpointProfile: Codable {
private enum CodingKeys: CodingKey {
case applicationId
case endpointId
case deviceToken
case effectiveDate
case isDebug
case isOptOut
case location
case demographic
case user
case attributes
case metrics
}

public convenience init(from decoder: Decoder) throws {
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.init(
applicationId: try container.decode(String.self, forKey: .applicationId),
endpointId: try container.decode(String.self, forKey: .endpointId),
deviceToken: try container.decodeIfPresent(DeviceToken.self, forKey: .deviceToken),
effectiveDate: try container.decode(Date.self, forKey: .effectiveDate),
isDebug: try container.decode(Bool.self, forKey: .isDebug),
isOptOut: try container.decode(Bool.self, forKey: .isOptOut),
location: try container.decode(PinpointClientTypes.EndpointLocation.self, forKey: .location),
demographic: try container.decode(PinpointClientTypes.EndpointDemographic.self, forKey: .demographic),
user: try container.decode(PinpointClientTypes.EndpointUser.self, forKey: .user),
attributes: try container.decode([String: [String]].self, forKey: .attributes),
metrics: try container.decode([String: Double].self, forKey: .metrics)
)
} catch {
print(error)
throw error
}
}

public func encode(to encoder: Encoder) throws {
do {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(applicationId, forKey: .applicationId)
try container.encode(endpointId, forKey: .endpointId)
try container.encodeIfPresent(deviceToken, forKey: .deviceToken)
try container.encode(effectiveDate, forKey: .effectiveDate)
try container.encode(isDebug, forKey: .isDebug)
try container.encode(isOptOut, forKey: .isOptOut)
try container.encode(location, forKey: .location)
try container.encode(demographic, forKey: .demographic)
try container.encode(user, forKey: .user)
try container.encode(attributes, forKey: .attributes)
try container.encode(metrics, forKey: .metrics)
} catch {
print(error)
throw error
}
}
}

0 comments on commit 0352f8d

Please sign in to comment.