diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointEndpointProfile.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointEndpointProfile.swift index 45ba969c2a..1a713b44b4 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointEndpointProfile.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointEndpointProfile.swift @@ -10,7 +10,7 @@ import AWSPinpoint import Foundation @_spi(InternalAWSPinpoint) -public class PinpointEndpointProfile: Codable { +public final class PinpointEndpointProfile { typealias DeviceToken = String var applicationId: String @@ -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, @@ -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 @@ -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]?) { @@ -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 + } } }