@@ -29,12 +29,6 @@ struct VariationDecision {
2929 var cmabUUID : String ?
3030}
3131
32- enum OperationType {
33- case async
34- case sync
35- }
36-
37- typealias OPType = OperationType
3832typealias UserProfile = OPTUserProfileService . UPProfile
3933
4034class DefaultDecisionService : OPTDecisionService {
@@ -44,14 +38,13 @@ class DefaultDecisionService: OPTDecisionService {
4438 let group : DispatchGroup = DispatchGroup ( )
4539 // thread-safe lazy logger load (after HandlerRegisterService ready)
4640 private let threadSafeLogger = ThreadSafeLogger ( )
47-
4841 // user-profile-service read-modify-write lock for supporting multiple clients
4942 static let upsRMWLock = DispatchQueue ( label: " ups-rmw " )
5043
5144 var logger : OPTLogger {
5245 return threadSafeLogger. logger
5346 }
54-
47+
5548 init ( userProfileService: OPTUserProfileService ,
5649 cmabService: CmabService = DefaultCmabService . createDefault ( ) ) {
5750 self . bucketer = DefaultBucketer ( )
@@ -74,24 +67,26 @@ class DefaultDecisionService: OPTDecisionService {
7467 /// - config: The project configuration containing experiment and feature details.
7568 /// - experiment: The CMAB experiment to evaluate.
7669 /// - user: The user context containing user ID and attributes.
70+ /// - bucketingId: User bucketing id
71+ /// - isAsync: Controls synchronous or asynchronous decision.
7772 /// - options: Optional decision options (e.g., ignore user profile service).
7873 /// - Returns: A `CMABDecisionResult` containing the CMAB decisions( variation id, cmabUUID) with reasons
7974
8075 private func getDecisionForCmabExperiment( config: ProjectConfig ,
8176 experiment: Experiment ,
8277 user: OptimizelyUserContext ,
8378 bucketingId: String ,
84- opType : OPType ,
79+ isAsync : Bool ,
8580 options: [ OptimizelyDecideOption ] ? ) -> DecisionResponse < VariationDecision > {
8681 let reasons = DecisionReasons ( options: options)
8782 guard let cmab = experiment. cmab else {
8883 logger. e ( " The experiment isn't a CMAB experiment " )
8984 return DecisionResponse ( result: nil , reasons: reasons)
9085 }
9186
92- guard opType == . async else {
87+ guard isAsync else {
9388 let info = LogMessage . cmabNotSupportedInSyncMode
94- logger. w ( info)
89+ logger. e ( info)
9590 reasons. addInfo ( info)
9691 return DecisionResponse ( result: nil , reasons: reasons)
9792 }
@@ -103,18 +98,18 @@ class DefaultDecisionService: OPTDecisionService {
10398 if let _reasons = bucketedResponse? . reasons {
10499 reasons. merge ( _reasons)
105100 }
106-
101+
107102 let entityId = bucketedResponse? . result
108103
109- // this means the user is not in the cmab experiment
104+ // This means the user is not in the cmab experiment
110105 if entityId == nil {
111106 let info = LogMessage . userNotInCmabExperiment ( user. userId, experiment. key)
112107 logger. d ( info)
113108 reasons. addInfo ( info)
114109 return DecisionResponse ( result: nil , reasons: reasons)
115110 }
116111
117- /// Fetch CMAB decision
112+ // Fetch CMAB decision
118113 let response = cmabService. getDecision ( config: config, userContext: user, ruleId: experiment. id, options: options ?? [ ] )
119114 var cmabDecision : CmabDecision ?
120115 switch response {
@@ -159,31 +154,14 @@ class DefaultDecisionService: OPTDecisionService {
159154 profileTracker? . loadUserProfile ( )
160155 }
161156
162- let response = getVariation ( config: config, experiment: experiment, user: user, userProfileTracker: profileTracker)
157+ // isAsync to `false` for backward compatibility
158+ let response = getVariation ( config: config, experiment: experiment, user: user, isAsync: false , userProfileTracker: profileTracker)
163159
164160 if ( !ignoreUPS) {
165161 profileTracker? . save ( )
166162 }
167163
168- return response
169- }
170-
171- /// Determines the variation for a user in an experiment, considering user profile and decision rules.
172- /// - Parameters:
173- /// - config: The project configuration.
174- /// - experiment: The experiment to evaluate.
175- /// - user: The user context.
176- /// - options: Optional decision options.
177- /// - userProfileTracker: Optional tracker for user profile data.
178- /// - Returns: A `DecisionResponse` with the variation (if any) and decision reasons.
179- func getVariation( config: ProjectConfig ,
180- experiment: Experiment ,
181- user: OptimizelyUserContext ,
182- options: [ OptimizelyDecideOption ] ? = nil ,
183- userProfileTracker: UserProfileTracker ? ) -> DecisionResponse < Variation > {
184- let decisionResponse = self . getVariation ( config: config, experiment: experiment, user: user, opType: . sync, userProfileTracker: userProfileTracker)
185-
186- return DecisionResponse ( result: decisionResponse. result? . variation, reasons: decisionResponse. reasons)
164+ return DecisionResponse ( result: response. result? . variation, reasons: response. reasons)
187165 }
188166
189167 /// Determines the variation for a user in an experiment, considering user profile and decision rules.
@@ -192,14 +170,14 @@ class DefaultDecisionService: OPTDecisionService {
192170 /// - experiment: The experiment to evaluate.
193171 /// - user: The user context.
194172 /// - options: Optional decision options.
195- /// - opType: Operation type, either sync or async
173+ /// - isAsync: Controls synchronous or asynchronous decision.
196174 /// - userProfileTracker: Optional tracker for user profile data.
197175 /// - Returns: A `DecisionResponse` with the variation (if any) and decision reasons.
198176 func getVariation( config: ProjectConfig ,
199177 experiment: Experiment ,
200178 user: OptimizelyUserContext ,
201179 options: [ OptimizelyDecideOption ] ? = nil ,
202- opType : OPType ,
180+ isAsync : Bool ,
203181 userProfileTracker: UserProfileTracker ? ) -> DecisionResponse < VariationDecision > {
204182 let reasons = DecisionReasons ( options: options)
205183 let userId = user. userId
@@ -241,7 +219,7 @@ class DefaultDecisionService: OPTDecisionService {
241219 reasons. addInfo ( info)
242220 }
243221
244- /// Load variation from tracker
222+ // Load variation from tracker
245223 if let profile = userProfileTracker? . userProfile,
246224 let variationId = getVariationIdFromProfile ( profile: profile, experimentId: experimentId) ,
247225 let variation = experiment. getVariation ( id: variationId) {
@@ -270,12 +248,12 @@ class DefaultDecisionService: OPTDecisionService {
270248 experiment: experiment,
271249 user: user,
272250 bucketingId: bucketingId,
273- opType : opType ,
251+ isAsync : isAsync ,
274252 options: options)
275253 reasons. merge ( cmabDecisionResponse. reasons)
276254 variationDecision = cmabDecisionResponse. result
277255 } else {
278- /// bucket user into a variation
256+ // bucket user into a variation
279257 let decisionResponse = bucketer. bucketExperiment ( config: config,
280258 experiment: experiment,
281259 bucketingId: bucketingId)
@@ -318,17 +296,25 @@ class DefaultDecisionService: OPTDecisionService {
318296 featureFlag: FeatureFlag ,
319297 user: OptimizelyUserContext ,
320298 options: [ OptimizelyDecideOption ] ? = nil ) -> DecisionResponse < FeatureDecision > {
321-
322- self . getVariationForFeature ( config: config, featureFlag: featureFlag, user: user, opType : . sync , options: options)
299+ // isAsync to `false` for backward compatibility
300+ self . getVariationForFeature ( config: config, featureFlag: featureFlag, user: user, isAsync : false , options: options)
323301 }
324302
303+ /// Determines the feature decision for a user for a specific feature flag.
304+ /// - Parameters:
305+ /// - config: The project configuration.
306+ /// - featureFlag: The feature flag to evaluate.
307+ /// - user: The user context.
308+ /// - isAsync: Controls synchronous or asynchronous decision.
309+ /// - options: Optional decision options.
310+ /// - Returns: A `DecisionResponse` with the feature decision (if any) and reasons.
325311 func getVariationForFeature( config: ProjectConfig ,
326312 featureFlag: FeatureFlag ,
327313 user: OptimizelyUserContext ,
328- opType : OPType ,
314+ isAsync : Bool ,
329315 options: [ OptimizelyDecideOption ] ? = nil ) -> DecisionResponse < FeatureDecision > {
330316
331- let response = getVariationForFeatureList ( config: config, featureFlags: [ featureFlag] , user: user, opType : opType , options: options) . first
317+ let response = getVariationForFeatureList ( config: config, featureFlags: [ featureFlag] , user: user, isAsync : isAsync , options: options) . first
332318
333319 guard response? . result != nil else {
334320 let reasons = response? . reasons ?? DecisionReasons ( options: options)
@@ -343,12 +329,13 @@ class DefaultDecisionService: OPTDecisionService {
343329 /// - config: The project configuration.
344330 /// - featureFlags: The list of feature flags to evaluate.
345331 /// - user: The user context.
332+ /// - isAsync: Controls synchronous or asynchronous decision
346333 /// - options: Optional decision options.
347334 /// - Returns: An array of `DecisionResponse` objects, each containing a feature decision and reasons.
348335 func getVariationForFeatureList( config: ProjectConfig ,
349336 featureFlags: [ FeatureFlag ] ,
350337 user: OptimizelyUserContext ,
351- opType : OPType = . sync ,
338+ isAsync : Bool ,
352339 options: [ OptimizelyDecideOption ] ? = nil ) -> [ DecisionResponse < FeatureDecision > ] {
353340
354341 let userId = user. userId
@@ -362,7 +349,7 @@ class DefaultDecisionService: OPTDecisionService {
362349 var decisions = [ DecisionResponse < FeatureDecision > ] ( )
363350
364351 for featureFlag in featureFlags {
365- let flagDecisionResponse = getDecisionForFlag ( config: config, featureFlag: featureFlag, user: user, userProfileTracker: profileTracker, opType : opType , options: options)
352+ let flagDecisionResponse = getDecisionForFlag ( config: config, featureFlag: featureFlag, user: user, userProfileTracker: profileTracker, isAsync : isAsync , options: options)
366353 decisions. append ( flagDecisionResponse)
367354 }
368355
@@ -380,13 +367,14 @@ class DefaultDecisionService: OPTDecisionService {
380367 /// - featureFlag: The feature flag to evaluate.
381368 /// - user: The user context.
382369 /// - userProfileTracker: Optional tracker for user profile data.
370+ /// - isAsync: Controls synchronous or asynchronous decision
383371 /// - options: Optional decision options.
384372 /// - Returns: A `DecisionResponse` with the feature decision (if any) and reasons.
385373 func getDecisionForFlag( config: ProjectConfig ,
386374 featureFlag: FeatureFlag ,
387375 user: OptimizelyUserContext ,
388376 userProfileTracker: UserProfileTracker ? = nil ,
389- opType : OPType ,
377+ isAsync : Bool ,
390378 options: [ OptimizelyDecideOption ] ? = nil ) -> DecisionResponse < FeatureDecision > {
391379 let reasons = DecisionReasons ( options: options)
392380
@@ -404,7 +392,7 @@ class DefaultDecisionService: OPTDecisionService {
404392 }
405393 }
406394
407- let flagExpDecision = getVariationForFeatureExperiments ( config: config, featureFlag: featureFlag, user: user, userProfileTracker: userProfileTracker, opType : opType , options: options)
395+ let flagExpDecision = getVariationForFeatureExperiments ( config: config, featureFlag: featureFlag, user: user, userProfileTracker: userProfileTracker, isAsync : isAsync , options: options)
408396 reasons. merge ( flagExpDecision. reasons)
409397
410398 if let decision = flagExpDecision. result {
@@ -427,13 +415,14 @@ class DefaultDecisionService: OPTDecisionService {
427415 /// - featureFlag: The feature flag to evaluate.
428416 /// - user: The user context.
429417 /// - userProfileTracker: Optional tracker for user profile data.
418+ /// - isAsync: Controls synchronous or asynchronous decision
430419 /// - options: Optional decision options.
431420 /// - Returns: A `DecisionResponse` with the feature decision (if any) and reasons.
432421 func getVariationForFeatureExperiments( config: ProjectConfig ,
433422 featureFlag: FeatureFlag ,
434423 user: OptimizelyUserContext ,
435424 userProfileTracker: UserProfileTracker ? = nil ,
436- opType : OPType = . sync ,
425+ isAsync : Bool ,
437426 options: [ OptimizelyDecideOption ] ? = nil ) -> DecisionResponse < FeatureDecision > {
438427 let reasons = DecisionReasons ( options: options)
439428
@@ -453,7 +442,7 @@ class DefaultDecisionService: OPTDecisionService {
453442 rule: experiment,
454443 user: user,
455444 userProfileTracker: userProfileTracker,
456- opType : opType ,
445+ isAsync : isAsync ,
457446 options: options)
458447 reasons. merge ( decisionResponse. reasons)
459448 if let result = decisionResponse. result {
@@ -563,7 +552,7 @@ class DefaultDecisionService: OPTDecisionService {
563552
564553 let userId = user. userId
565554 let attributes = user. attributes
566-
555+
567556 // Acquire bucketingId .
568557 let bucketingId = getBucketingId ( userId: userId, attributes: attributes)
569558 var bucketedVariation : Variation ?
@@ -614,7 +603,7 @@ class DefaultDecisionService: OPTDecisionService {
614603 rule: Experiment ,
615604 user: OptimizelyUserContext ,
616605 userProfileTracker: UserProfileTracker ? ,
617- opType : OPType = . sync ,
606+ isAsync : Bool ,
618607 options: [ OptimizelyDecideOption ] ? = nil ) -> DecisionResponse < VariationDecision > {
619608 let reasons = DecisionReasons ( options: options)
620609 // check forced-decision first
@@ -632,7 +621,7 @@ class DefaultDecisionService: OPTDecisionService {
632621 experiment: rule,
633622 user: user,
634623 options: options,
635- opType : opType ,
624+ isAsync : isAsync ,
636625 userProfileTracker: userProfileTracker)
637626 let variationResult = decisionResponse. result
638627 reasons. merge ( decisionResponse. reasons)
@@ -656,7 +645,7 @@ class DefaultDecisionService: OPTDecisionService {
656645 options: [ OptimizelyDecideOption ] ? = nil ) -> DecisionResponse < ( Variation ? , Bool ) > {
657646 let reasons = DecisionReasons ( options: options)
658647 var skipToEveryoneElse = false
659-
648+
660649 // check forced-decision first
661650
662651 let rule = rules [ ruleIndex]
0 commit comments