Skip to content

Commit 6589d6a

Browse files
author
Yasir Ali
committed
Merge branch 'master' into yasir/ondecision-activate-getvariation
2 parents 5f60ce2 + 1f38e54 commit 6589d6a

20 files changed

+1185
-159
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>FILEHEADER</key>
6+
<string>
7+
/****************************************************************************
8+
* Copyright 2019, Optimizely, Inc. and contributors *
9+
* *
10+
* Licensed under the Apache License, Version 2.0 (the "License"); *
11+
* you may not use this file except in compliance with the License. *
12+
* You may obtain a copy of the License at *
13+
* *
14+
* http://www.apache.org/licenses/LICENSE-2.0 *
15+
* *
16+
* Unless required by applicable law or agreed to in writing, software *
17+
* distributed under the License is distributed on an "AS IS" BASIS, *
18+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
19+
* See the License for the specific language governing permissions and *
20+
* limitations under the License. *
21+
***************************************************************************/
22+
</string>
23+
</dict>
24+
</plist>
25+

OptimizelySDK/Customization/DefaultEventDispatcher.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ open class DefaultEventDispatcher : BackgroundingCallbacks, OPTEventDispatcher {
6767
}
6868

6969
deinit {
70-
if let timer = timer.property {
70+
timer.performAtomic() { (timer) in
7171
timer.invalidate()
7272
}
7373
unsubscribe()
@@ -206,7 +206,7 @@ open class DefaultEventDispatcher : BackgroundingCallbacks, OPTEventDispatcher {
206206
}
207207

208208
func applicationDidEnterBackground() {
209-
if let timer = timer.property {
209+
timer.performAtomic() { (timer) in
210210
timer.invalidate()
211211
}
212212
timer.property = nil
@@ -231,7 +231,9 @@ open class DefaultEventDispatcher : BackgroundingCallbacks, OPTEventDispatcher {
231231
DispatchQueue.main.async {
232232
self.timer.property = Timer.scheduledTimer(withTimeInterval: self.timerInterval, repeats: true) { (timer) in
233233
if self.dataStore.count == 0 {
234-
self.timer.property?.invalidate()
234+
self.timer.performAtomic() { (timer) in
235+
timer.invalidate()
236+
}
235237
self.timer.property = nil
236238
}
237239
else {

OptimizelySDK/Extensions/OptimizelyManager+Extension.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ extension OptimizelyManager {
1212
func registerServices(sdkKey:String,
1313
logger:OPTLogger,
1414
eventDispatcher:OPTEventDispatcher,
15-
userProfileService:OPTUserProfileService,
1615
datafileHandler:OPTDatafileHandler,
17-
bucketer:OPTBucketer,
1816
decisionService:OPTDecisionService,
1917
notificationCenter:OPTNotificationCenter) {
2018
// bind it as a non-singleton. so, we will create an instance anytime injected.
@@ -26,10 +24,6 @@ extension OptimizelyManager {
2624
// this is bound a reusable singleton. so, if we re-initalize, we will keep this.
2725
HandlerRegistryService.shared.registerBinding(binder:Binder<OPTNotificationCenter>(service: OPTNotificationCenter.self).singetlon().reInitializeStrategy(strategy: .reUse).using(instance:notificationCenter).sdkKey(key: sdkKey))
2826

29-
// this is a singleton but it has a reIntializeStrategy of reCreate. So, we create a new
30-
// instance on re-initialize.
31-
HandlerRegistryService.shared.registerBinding(binder:Binder<OPTBucketer>(service: OPTBucketer.self).singetlon().using(instance:bucketer).sdkKey(key: sdkKey))
32-
3327
// the decision service is also a singleton that will reCreate on re-initalize
3428
HandlerRegistryService.shared.registerBinding(binder:Binder<OPTDecisionService>(service: OPTDecisionService.self).singetlon().using(instance:decisionService).sdkKey(key: sdkKey))
3529

@@ -39,9 +33,5 @@ extension OptimizelyManager {
3933
// This is a singleton and might be a good candidate for reuse. The handler supports mulitple
4034
// sdk keys without having to be created for every key.
4135
HandlerRegistryService.shared.registerBinding(binder:Binder<OPTDatafileHandler>(service: OPTDatafileHandler.self).singetlon().reInitializeStrategy(strategy: .reUse).to(factory: type(of:datafileHandler).init).using(instance: datafileHandler).sdkKey(key: sdkKey))
42-
43-
// the user profile service is also a singleton using eh passed in version.
44-
HandlerRegistryService.shared.registerBinding(binder:Binder<OPTUserProfileService>(service: OPTUserProfileService.self).singetlon().reInitializeStrategy(strategy:.reUse).using(instance:userProfileService).to(factory: type(of:userProfileService).init).sdkKey(key: sdkKey))
45-
4636
}
4737
}

OptimizelySDK/Implementation/DefaultBucketer.swift

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,14 @@ class DefaultBucketer : OPTBucketer {
2222
let MAX_HASH_SEED:UInt64 = 1
2323
var MAX_HASH_VALUE:UInt64?
2424

25-
private var config:ProjectConfig!
2625
private lazy var logger = HandlerRegistryService.shared.injectLogger()
2726

28-
internal required init(config:ProjectConfig) {
29-
self.config = config
30-
31-
MAX_HASH_VALUE = MAX_HASH_SEED << 32
32-
}
33-
3427
// [Jae]: let be configured after initialized (with custom DecisionHandler set up on OPTManger initialization)
3528
init() {
3629
MAX_HASH_VALUE = MAX_HASH_SEED << 32
3730
}
3831

39-
func initialize(config:ProjectConfig) {
40-
self.config = config
41-
}
42-
43-
func bucketToExperiment(group: Group, bucketingId: String) -> Experiment? {
32+
func bucketToExperiment(config:ProjectConfig, group: Group, bucketingId: String) -> Experiment? {
4433
let hashId = makeHashIdFromBucketingId(bucketingId: bucketingId, entityId: group.id)
4534
let bucketValue = self.generateBucketValue(bucketingId: hashId)
4635

@@ -73,7 +62,7 @@ class DefaultBucketer : OPTBucketer {
7362
return nil
7463
}
7564

76-
func bucketExperiment(experiment: Experiment, bucketingId: String) -> Variation? {
65+
func bucketExperiment(config:ProjectConfig, experiment: Experiment, bucketingId: String) -> Variation? {
7766
var ok = true
7867
// check for mutex
7968
let group = config.project.groups.filter({ if let _ = $0.experiments.filter({$0.id == experiment.id }).first { return true } else { return false }}).first
@@ -83,7 +72,7 @@ class DefaultBucketer : OPTBucketer {
8372
case .overlapping:
8473
break;
8574
case .random:
86-
let mutexExperiment = bucketToExperiment(group: group, bucketingId: bucketingId)
75+
let mutexExperiment = bucketToExperiment(config: config, group: group, bucketingId: bucketingId)
8776
if let mutexExperiment = mutexExperiment, mutexExperiment.id == experiment.id {
8877
ok = true
8978
}

OptimizelySDK/Implementation/DefaultDatafileHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class DefaultDatafileHandler : OPTDatafileHandler {
5555
return result
5656
}
5757

58-
func downloadDatafile(sdkKey: String, completionHandler: @escaping (Result<Data?, DatafileDownloadError>) -> Void) {
58+
open func downloadDatafile(sdkKey: String, completionHandler: @escaping (Result<Data?, DatafileDownloadError>) -> Void) {
5959
let config = URLSessionConfiguration.ephemeral
6060
let session = URLSession(configuration: config)
6161
let str = String(format: DefaultDatafileHandler.endPointStringFormat, sdkKey)

OptimizelySDK/Implementation/DefaultDecisionService.swift

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,16 @@
1717
import Foundation
1818

1919
class DefaultDecisionService : OPTDecisionService {
20-
var config:ProjectConfig!
21-
var bucketer:OPTBucketer!
22-
var userProfileService:OPTUserProfileService!
23-
24-
internal required init(config:ProjectConfig, bucketer:OPTBucketer, userProfileService:OPTUserProfileService) {
25-
self.config = config
26-
self.bucketer = bucketer
27-
self.userProfileService = userProfileService
28-
}
2920

30-
// [Jae]: let be configured after initialized (with custom DecisionHandler set up on OPTManger initialization)
31-
init() {}
21+
let bucketer:OPTBucketer
22+
let userProfileService:OPTUserProfileService
3223

33-
func initialize(config:ProjectConfig, bucketer:OPTBucketer, userProfileService:OPTUserProfileService) {
34-
self.config = config
35-
self.bucketer = bucketer
24+
init(userProfileService:OPTUserProfileService) {
25+
self.bucketer = DefaultBucketer()
3626
self.userProfileService = userProfileService
3727
}
38-
39-
func getVariation(userId:String, experiment: Experiment, attributes: OptimizelyAttributes) -> Variation? {
28+
29+
func getVariation(config:ProjectConfig, userId:String, experiment: Experiment, attributes: OptimizelyAttributes) -> Variation? {
4030
let experimentId = experiment.id;
4131

4232
// Acquire bucketingId .
@@ -68,12 +58,13 @@ class DefaultDecisionService : OPTDecisionService {
6858
var bucketedVariation:Variation?
6959
// ---- check if the user passes audience targeting before bucketing ----
7060
if let result = isInExperiment(
61+
config: config,
7162
experiment:experiment,
7263
userId:userId,
7364
attributes:attributes), result == true {
7465

7566
// bucket user into a variation
76-
bucketedVariation = bucketer.bucketExperiment(experiment: experiment, bucketingId:bucketingId)
67+
bucketedVariation = bucketer.bucketExperiment(config: config, experiment: experiment, bucketingId:bucketingId)
7768

7869
if let bucketedVariation = bucketedVariation {
7970
// save to user profile
@@ -85,7 +76,7 @@ class DefaultDecisionService : OPTDecisionService {
8576

8677
}
8778

88-
func isInExperiment(experiment:Experiment, userId:String, attributes: OptimizelyAttributes) -> Bool? {
79+
func isInExperiment(config:ProjectConfig, experiment:Experiment, userId:String, attributes: OptimizelyAttributes) -> Bool? {
8980

9081
if let conditions = experiment.audienceConditions {
9182
switch conditions {
@@ -119,45 +110,46 @@ class DefaultDecisionService : OPTDecisionService {
119110
return true
120111
}
121112

122-
func getExperimentInGroup(group:Group, bucketingId:String) -> Experiment? {
123-
let experiment = bucketer.bucketToExperiment(group:group, bucketingId:bucketingId)
113+
func getExperimentInGroup(config:ProjectConfig, group:Group, bucketingId:String) -> Experiment? {
114+
let experiment = bucketer.bucketToExperiment(config: config, group:group, bucketingId:bucketingId)
124115
if let _ = experiment {
125116
// log
126117
}
127118

128119
return experiment;
129120
}
130121

131-
func getVariationForFeature(featureFlag:FeatureFlag, userId:String, attributes: OptimizelyAttributes) -> (experiment:Experiment?, variation:Variation?)? {
122+
func getVariationForFeature(config:ProjectConfig, featureFlag:FeatureFlag, userId:String, attributes: OptimizelyAttributes) -> (experiment:Experiment?, variation:Variation?)? {
132123
//Evaluate in this order:
133124

134125
//1. Attempt to bucket user into experiment using feature flag.
135126
// Check if the feature flag is under an experiment and the the user is bucketed into one of these experiments
136-
if let variation = getVariationForFeatureExperiment(featureFlag: featureFlag, userId:userId, attributes:attributes) {
127+
if let variation = getVariationForFeatureExperiment(config: config, featureFlag: featureFlag, userId:userId, attributes:attributes) {
137128
return variation
138129
}
139130

140131
//2. Attempt to bucket user into rollout using the feature flag.
141132
// Check if the feature flag has rollout and the user is bucketed into one of it's rules
142-
if let variation = getVariationForFeatureRollout(featureFlag: featureFlag, userId:userId, attributes:attributes) {
133+
if let variation = getVariationForFeatureRollout(config: config, featureFlag: featureFlag, userId:userId, attributes:attributes) {
143134
return (nil, variation)
144135
}
145136

146137
return nil;
147138

148139
}
149140

150-
func getVariationForFeatureGroup(featureFlag: FeatureFlag,
141+
func getVariationForFeatureGroup(config:ProjectConfig,
142+
featureFlag: FeatureFlag,
151143
groupId: String,
152144
userId: String,
153145
attributes: OptimizelyAttributes) -> (experiment:Experiment?, variation:Variation?)? {
154146

155147
let bucketing_id = getBucketingId(userId:userId, attributes:attributes)
156148
if let group = config.project.groups.filter({$0.id == groupId}).first {
157149

158-
if let experiment = getExperimentInGroup(group: group, bucketingId:bucketing_id),
150+
if let experiment = getExperimentInGroup(config: config, group: group, bucketingId:bucketing_id),
159151
featureFlag.experimentIds.contains(experiment.id),
160-
let variation = getVariation(userId:userId, experiment:experiment, attributes:attributes) {
152+
let variation = getVariation(config: config, userId:userId, experiment:experiment, attributes:attributes) {
161153
// log
162154
return (experiment,variation)
163155
}
@@ -169,7 +161,8 @@ class DefaultDecisionService : OPTDecisionService {
169161
return nil
170162
}
171163

172-
func getVariationForFeatureExperiment(featureFlag: FeatureFlag,
164+
func getVariationForFeatureExperiment(config:ProjectConfig,
165+
featureFlag: FeatureFlag,
173166
userId: String,
174167
attributes: OptimizelyAttributes) -> (experiment:Experiment?, variation:Variation?)? {
175168

@@ -178,14 +171,15 @@ class DefaultDecisionService : OPTDecisionService {
178171
// Evaluate each experiment ID and return the first bucketed experiment variation
179172
for experimentId in experimentIds {
180173
if let experiment = config.getExperiment(id: experimentId),
181-
let variation = getVariation(userId: userId, experiment: experiment, attributes: attributes) {
174+
let variation = getVariation(config: config, userId: userId, experiment: experiment, attributes: attributes) {
182175
return (experiment,variation)
183176
}
184177
}
185178
return nil;
186179
}
187180

188-
func getVariationForFeatureRollout(featureFlag: FeatureFlag,
181+
func getVariationForFeatureRollout(config:ProjectConfig,
182+
featureFlag: FeatureFlag,
189183
userId: String,
190184
attributes: OptimizelyAttributes) -> Variation? {
191185

@@ -200,16 +194,16 @@ class DefaultDecisionService : OPTDecisionService {
200194
let rolloutRules = rollout.experiments
201195
// Evaluate all rollout rules except for last one
202196
for experiment in rolloutRules[0..<rolloutRules.count.advanced(by: -1)] {
203-
if isInExperiment(experiment: experiment, userId: userId, attributes: attributes) ?? false {
204-
if let variation = bucketer.bucketExperiment(experiment: experiment, bucketingId: bucketingId) {
197+
if isInExperiment(config: config, experiment: experiment, userId: userId, attributes: attributes) ?? false {
198+
if let variation = bucketer.bucketExperiment(config:config, experiment: experiment, bucketingId: bucketingId) {
205199
return variation
206200
}
207201
}
208202
}
209203
// Evaluate fall back rule / last rule now
210204
let experiment = rolloutRules[rolloutRules.count - 1];
211-
if isInExperiment(experiment: experiment, userId: userId, attributes: attributes) ?? false {
212-
return bucketer.bucketExperiment(experiment: experiment, bucketingId: bucketingId)
205+
if isInExperiment(config: config, experiment: experiment, userId: userId, attributes: attributes) ?? false {
206+
return bucketer.bucketExperiment(config: config, experiment: experiment, bucketingId: bucketingId)
213207
}
214208

215209
return nil

0 commit comments

Comments
 (0)