Skip to content

Commit 62ae49b

Browse files
[FSSDK-10761] feat: make vuid as opt-in (#556)
VUID optln added
1 parent 3635373 commit 62ae49b

16 files changed

+310
-219
lines changed

OptimizelySwiftSDK.xcodeproj/project.pbxproj

Lines changed: 39 additions & 39 deletions
Large diffs are not rendered by default.

Sources/ODP/OdpEventManager.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ open class OdpEventManager {
4949

5050
// MARK: - events
5151

52-
func registerVUID(vuid: String) {
52+
func sendInitializedEvent(vuid: String) {
5353
sendEvent(type: Constants.ODP.eventType,
5454
action: "client_initialized",
5555
identifiers: [
@@ -58,8 +58,11 @@ open class OdpEventManager {
5858
data: [:])
5959
}
6060

61-
func identifyUser(vuid: String, userId: String?) {
62-
var identifiers = [Constants.ODP.keyForVuid: vuid]
61+
func identifyUser(vuid: String?, userId: String?) {
62+
var identifiers = [String: String]()
63+
if let _vuid = vuid {
64+
identifiers[Constants.ODP.keyForVuid] = _vuid
65+
}
6366
if let userId = userId {
6467
identifiers[Constants.ODP.keyForUserId] = userId
6568
}

Sources/ODP/OdpManager.swift

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@ import Foundation
1818

1919
public class OdpManager {
2020
var enabled: Bool
21-
var vuidManager: OdpVuidManager
22-
2321
var odpConfig: OdpConfig!
2422
var segmentManager: OdpSegmentManager!
2523
var eventManager: OdpEventManager!
2624

2725
let logger = OPTLoggerFactory.getLogger()
2826

29-
var vuid: String {
30-
return vuidManager.vuid
31-
}
27+
var vuid: String?
3228

3329
/// OdpManager init
3430
/// - Parameters:
@@ -42,6 +38,7 @@ public class OdpManager {
4238
/// - eventManager: ODPEventManager
4339
public init(sdkKey: String,
4440
disable: Bool,
41+
vuid: String? = nil,
4542
cacheSize: Int,
4643
cacheTimeoutInSecs: Int,
4744
timeoutForSegmentFetchInSecs: Int? = nil,
@@ -50,8 +47,7 @@ public class OdpManager {
5047
eventManager: OdpEventManager? = nil) {
5148

5249
self.enabled = !disable
53-
self.vuidManager = OdpVuidManager.shared
54-
50+
self.vuid = vuid
5551
guard enabled else {
5652
logger.i(.odpNotEnabled)
5753
return
@@ -65,8 +61,6 @@ public class OdpManager {
6561
self.odpConfig = OdpConfig()
6662
self.segmentManager.odpConfig = odpConfig
6763
self.eventManager.odpConfig = odpConfig
68-
69-
self.eventManager.registerVUID(vuid: self.vuidManager.vuid)
7064
}
7165

7266
func fetchQualifiedSegments(userId: String,
@@ -77,7 +71,7 @@ public class OdpManager {
7771
return
7872
}
7973

80-
let userKey = OdpVuidManager.isVuid(userId) ? Constants.ODP.keyForVuid : Constants.ODP.keyForUserId
74+
let userKey = VuidManager.isVuid(userId) ? Constants.ODP.keyForVuid : Constants.ODP.keyForUserId
8175
let userValue = userId
8276

8377
segmentManager.fetchQualifiedSegments(userKey: userKey,
@@ -97,15 +91,13 @@ public class OdpManager {
9791
return
9892
}
9993

100-
var vuid = vuidManager.vuid
101-
var fsUserId: String? = userId
102-
if OdpVuidManager.isVuid(userId) {
94+
if VuidManager.isVuid(userId) {
10395
// overwrite if userId is vuid (when userContext is created with vuid)
104-
vuid = userId
105-
fsUserId = nil
96+
eventManager.identifyUser(vuid: userId, userId: nil)
97+
} else {
98+
eventManager.identifyUser(vuid: self.vuid, userId: userId)
10699
}
107100

108-
eventManager.identifyUser(vuid: vuid, userId: fsUserId)
109101
}
110102

111103
/// Send an event to the ODP server.
@@ -125,15 +117,13 @@ public class OdpManager {
125117

126118
let typeUpdated = (type ?? "").isEmpty ? Constants.ODP.eventType : type!
127119

128-
// add vuid to all events by default
129-
130120
var identifiersUpdated = identifiers
131-
if identifiers[Constants.ODP.keyForVuid] == nil {
132-
identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.vuid
121+
122+
if identifiers[Constants.ODP.keyForVuid] == nil, let _vuid = vuid {
123+
identifiersUpdated[Constants.ODP.keyForVuid] = _vuid
133124
}
134125

135126
// replace aliases (fs-user-id, FS_USER_ID, FS-USER-ID) with "fs_user_id".
136-
137127
for (idKey, idValue) in identifiersUpdated {
138128
if idKey == Constants.ODP.keyForUserId { break }
139129

Sources/ODP/OptimizelySdkSettings.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public struct OptimizelySdkSettings {
2727
let timeoutForOdpEventInSecs: Int
2828
/// ODP features are disabled if this is set to true.
2929
let disableOdp: Bool
30+
/// VUID is enabled if this is set to true.
31+
let enableVuid: Bool
3032

3133
/// Optimizely SDK Settings
3234
///
@@ -36,20 +38,23 @@ public struct OptimizelySdkSettings {
3638
/// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero.
3739
/// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero.
3840
/// - disableOdp: Set this flag to true (default = false) to disable ODP features
41+
/// - enableVuid: Set this flag to true (default = false) to enable vuid.
3942
/// - sdkName: Set this flag to override sdkName included in events
4043
/// - sdkVersion: Set this flag to override sdkVersion included in events
4144
public init(segmentsCacheSize: Int = 100,
4245
segmentsCacheTimeoutInSecs: Int = 600,
4346
timeoutForSegmentFetchInSecs: Int = 10,
4447
timeoutForOdpEventInSecs: Int = 10,
4548
disableOdp: Bool = false,
49+
enableVuid: Bool = false,
4650
sdkName: String? = nil,
4751
sdkVersion: String? = nil) {
4852
self.segmentsCacheSize = segmentsCacheSize
4953
self.segmentsCacheTimeoutInSecs = segmentsCacheTimeoutInSecs
5054
self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs
5155
self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs
5256
self.disableOdp = disableOdp
57+
self.enableVuid = enableVuid
5358
if let _sdkName = sdkName, _sdkName != "" {
5459
Utils.swiftSdkClientName = _sdkName
5560
}

Sources/Optimizely+Decide/OptimizelyClient+Decide.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ extension OptimizelyClient {
3535
///
3636
/// - Parameter attributes: A map of attribute names to current user attribute values.
3737
/// - Returns: An OptimizelyUserContext associated with this OptimizelyClient
38-
public func createUserContext(attributes: [String: Any]? = nil) -> OptimizelyUserContext {
38+
public func createUserContext(attributes: [String: Any]? = nil) -> OptimizelyUserContext? {
39+
guard enableVuid, let vuid = self.vuid else {
40+
logger.e("Vuid is not enabled or invalid VUID. User context not created.")
41+
return nil
42+
}
3943
return OptimizelyUserContext(optimizely: self, userId: vuid, attributes: attributes)
4044
}
4145

Sources/Optimizely+Decide/OptimizelyUserContext.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ public class OptimizelyUserContext {
8484
self.atomicAttributes = AtomicProperty(property: attributes, lock: lock)
8585
self.atomicForcedDecisions = AtomicProperty(property: nil, lock: lock)
8686
self.atomicQualifiedSegments = AtomicProperty(property: nil, lock: lock)
87-
8887
if identify {
8988
// async call so event building overhead is not blocking context creation
9089
lock.async {

Sources/Optimizely/OptimizelyClient.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ open class OptimizelyClient: NSObject {
6060
var decisionService: OPTDecisionService!
6161
public var notificationCenter: OPTNotificationCenter?
6262
public var odpManager: OdpManager!
63+
private var vuidManager: VuidManager!
6364
let sdkSettings: OptimizelySdkSettings
6465

6566
// MARK: - Public interfaces
@@ -91,13 +92,15 @@ open class OptimizelyClient: NSObject {
9192
self.defaultDecideOptions = defaultDecideOptions ?? []
9293

9394
super.init()
94-
95+
self.vuidManager = VuidManager.shared
96+
self.vuidManager.configure(enable: self.sdkSettings.enableVuid)
9597
self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey,
9698
disable: sdkSettings.disableOdp,
9799
cacheSize: sdkSettings.segmentsCacheSize,
98100
cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs,
99101
timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs,
100102
timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs)
103+
self.odpManager.vuid = vuidManager.vuid
101104
let userProfileService = userProfileService ?? DefaultUserProfileService()
102105
let logger = logger ?? DefaultLogger()
103106
type(of: logger).logLevel = defaultLogLevel ?? .info
@@ -115,6 +118,16 @@ open class OptimizelyClient: NSObject {
115118
self.decisionService = HandlerRegistryService.shared.injectDecisionService(sdkKey: self.sdkKey)
116119
self.notificationCenter = HandlerRegistryService.shared.injectNotificationCenter(sdkKey: self.sdkKey)
117120

121+
if let _vuid = self.vuidManager.vuid {
122+
try? sendOdpEvent(type: Constants.ODP.eventType,
123+
action: "client_initialized",
124+
identifiers: [
125+
Constants.ODP.keyForVuid: _vuid
126+
],
127+
data: [:])
128+
}
129+
130+
118131
logger.d("SDK Version: \(version)")
119132
}
120133

@@ -971,8 +984,12 @@ extension OptimizelyClient {
971984
}
972985

973986
/// the device vuid (read only)
974-
public var vuid: String {
975-
return odpManager.vuid
987+
public var vuid: String? {
988+
return self.vuidManager.vuid
989+
}
990+
991+
public var enableVuid: Bool {
992+
return self.vuidManager.enable
976993
}
977994

978995
func identifyUserToOdp(userId: String) {

Sources/ODP/OdpVuidManager.swift renamed to Sources/Optimizely/VuidManager.swift

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,22 @@
1616

1717
import Foundation
1818

19-
class OdpVuidManager {
20-
var vuid: String = ""
19+
public class VuidManager {
20+
private var _vuid: String = ""
21+
private(set) var enable: Bool = false
2122
let logger = OPTLoggerFactory.getLogger()
22-
23+
2324
// a single vuid should be shared for all SDK instances
24-
static let shared = OdpVuidManager()
25+
public static let shared = VuidManager()
2526

26-
init() {
27-
self.vuid = load()
27+
public func configure(enable: Bool) {
28+
self.enable = enable
29+
if enable {
30+
self._vuid = load()
31+
} else {
32+
self.remove()
33+
self._vuid = ""
34+
}
2835
}
2936

3037
static var newVuid: String {
@@ -35,16 +42,24 @@ class OdpVuidManager {
3542
let vuid = (vuidFull.count <= maxLength) ? vuidFull : String(vuidFull.prefix(maxLength))
3643
return vuid
3744
}
38-
45+
3946
static func isVuid(_ visitorId: String) -> Bool {
40-
return visitorId.starts(with: "vuid_")
47+
return visitorId.lowercased().starts(with: "vuid_")
4148
}
4249

4350
}
4451

4552
// MARK: - VUID Store
4653

47-
extension OdpVuidManager {
54+
extension VuidManager {
55+
public var vuid: String? {
56+
if self.enable {
57+
return _vuid
58+
} else {
59+
logger.w("VUID is not enabled.")
60+
return nil
61+
}
62+
}
4863

4964
private var keyForVuid: String {
5065
return "optimizely-vuid"
@@ -55,11 +70,16 @@ extension OdpVuidManager {
5570
return oldVuid
5671
}
5772

58-
let vuid = OdpVuidManager.newVuid
73+
let vuid = VuidManager.newVuid
5974
save(vuid: vuid)
6075
return vuid
6176
}
62-
77+
78+
private func remove() {
79+
UserDefaults.standard.set(nil, forKey: keyForVuid)
80+
UserDefaults.standard.synchronize()
81+
}
82+
6383
private func save(vuid: String) {
6484
UserDefaults.standard.set(vuid, forKey: keyForVuid)
6585
UserDefaults.standard.synchronize()

Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ class OptimizelyClientTests_Decide: XCTestCase {
2424
super.setUp()
2525

2626
let datafile = OTUtils.loadJSONDatafile("api_datafile")!
27-
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey)
27+
let settings = OptimizelySdkSettings(enableVuid: true)
28+
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings)
29+
2830
try! optimizely.start(datafile: datafile)
2931
}
3032

@@ -54,11 +56,11 @@ class OptimizelyClientTests_Decide: XCTestCase {
5456

5557
let user = optimizely.createUserContext(attributes: attributes)
5658

57-
XCTAssert(user.optimizely == optimizely)
58-
XCTAssert(user.userId == optimizely.vuid, "vuid should be used as the default userId when not given")
59-
XCTAssert(user.attributes["country"] as! String == "us")
60-
XCTAssert(user.attributes["age"] as! Int == 100)
61-
XCTAssert(user.attributes["old"] as! Bool == true)
59+
XCTAssert(user?.optimizely == optimizely)
60+
XCTAssert(user?.userId == optimizely.vuid, "vuid should be used as the default userId when not given")
61+
XCTAssert(user?.attributes["country"] as! String == "us")
62+
XCTAssert(user?.attributes["age"] as! Int == 100)
63+
XCTAssert(user?.attributes["old"] as! Bool == true)
6264
}
6365

6466
func testCreateUserContext_multiple() {

Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,19 @@ class OptimizelyClientTests_ODP: XCTestCase {
187187

188188
// MARK: - vuid
189189

190-
func testVuid() {
191-
XCTAssert(optimizely.vuid.starts(with: "vuid_"))
190+
func testVuidEnabled() {
191+
let settings = OptimizelySdkSettings(enableVuid: true)
192+
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings)
193+
XCTAssertTrue(optimizely.enableVuid)
194+
XCTAssert(optimizely.vuid!.starts(with: "vuid_"))
192195
}
193196

197+
func testVuidDiabled() {
198+
// Default client vuid diabled
199+
XCTAssertFalse(optimizely.enableVuid)
200+
XCTAssertNil(optimizely.vuid)
201+
}
202+
194203
// MARK: - OdpConfig Update
195204

196205
func testUpdateOpdConfigCalled_wheneverProjectConfigUpdated_initialOrPolling() {

Tests/OptimizelyTests-Common/OdpEventManagerTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class OdpEventManagerTests: XCTestCase {
8989
}
9090

9191
func testRegisterVUID_noApiKey() {
92-
manager.registerVUID(vuid: "v1")
92+
manager.sendInitializedEvent(vuid: "v1")
9393

9494
XCTAssertEqual(1, manager.eventQueue.count)
9595

@@ -149,7 +149,7 @@ class OdpEventManagerTests: XCTestCase {
149149

150150
XCTAssertTrue(manager.odpConfig.eventQueueingAllowed, "initially datafile not ready and assumed queueing is allowed")
151151

152-
manager.registerVUID(vuid: "v1") // each of these will try to flush
152+
manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush
153153
manager.identifyUser(vuid: "v1", userId: "u1")
154154
manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:])
155155

@@ -184,7 +184,7 @@ class OdpEventManagerTests: XCTestCase {
184184

185185
XCTAssertTrue(manager.odpConfig.eventQueueingAllowed, "initially datafile not ready and assumed queueing is allowed")
186186

187-
manager.registerVUID(vuid: "v1") // each of these will try to flush
187+
manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush
188188
manager.identifyUser(vuid: "v1", userId: "u1")
189189
manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:])
190190

@@ -215,7 +215,7 @@ class OdpEventManagerTests: XCTestCase {
215215
func testFlush_maxSize() {
216216
manager.maxQueueSize = 2
217217

218-
manager.registerVUID(vuid: "v1") // each of these will try to flush
218+
manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush
219219
manager.identifyUser(vuid: "v1", userId: "u1")
220220
manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:])
221221

0 commit comments

Comments
 (0)