Skip to content

Commit 8b0d21b

Browse files
some refactor or repeat code and add a eventEmitterQueue for faster activates and tracks
1 parent 546fcc9 commit 8b0d21b

File tree

2 files changed

+114
-79
lines changed

2 files changed

+114
-79
lines changed

OptimizelySDK/Data Model/ProjectConfig.swift

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class ProjectConfig {
2727
private var whitelistUsers = [String: [String: String]]()
2828
private var experimentFeatureMap = [String: [String]]()
2929

30+
var experimentKeyMap:[String:Experiment]?
31+
var eventKeyMap:[String:Event]?
32+
33+
var _allExperiments:[Experiment]?
34+
3035
init(datafile: Data) throws {
3136
do {
3237
self.project = try JSONDecoder().decode(Project.self, from: datafile)
@@ -37,6 +42,12 @@ class ProjectConfig {
3742
throw OptimizelyError.dataFileVersionInvalid(self.project.version)
3843
}
3944
generateExperimentFeatureMap()
45+
46+
experimentKeyMap = [String:Experiment]()
47+
_ = allExperiments.map({experimentKeyMap?[$0.key] = $0})
48+
49+
eventKeyMap = [String:Event]()
50+
_ = project.events.map({eventKeyMap?[$0.key] = $0 })
4051
}
4152

4253
convenience init(datafile: String) throws {
@@ -92,17 +103,18 @@ extension ProjectConfig {
92103
}
93104

94105
private func generateExperimentFeatureMap() {
95-
for feature in project.featureFlags {
96-
for id in feature.experimentIds {
97-
if var featureIdArray = experimentFeatureMap[id] {
98-
featureIdArray.append(feature.id)
99-
experimentFeatureMap[id] = featureIdArray
106+
experimentFeatureMap = [String:[String]]()
107+
_ = project.featureFlags.map({ (ff) in
108+
ff.experimentIds.map({
109+
if var arr = self.experimentFeatureMap[$0] {
110+
arr.append(ff.id)
111+
self.experimentFeatureMap[$0] = arr
100112
}
101113
else {
102-
experimentFeatureMap[id] = [feature.id]
114+
self.experimentFeatureMap[$0] = [ff.id]
103115
}
104-
}
105-
}
116+
})
117+
})
106118
}
107119
}
108120

@@ -114,6 +126,9 @@ extension ProjectConfig {
114126
* Get an Experiment object for a key.
115127
*/
116128
func getExperiment(key: String) -> Experiment? {
129+
if let experimentMap = experimentKeyMap {
130+
return experimentMap[key]
131+
}
117132
return allExperiments.filter { $0.key == key }.first
118133
}
119134

@@ -156,7 +171,7 @@ extension ProjectConfig {
156171
* Gets an event for a corresponding event key
157172
*/
158173
func getEvent(key: String) -> Event? {
159-
return project.events.filter{ $0.key == key }.first
174+
return eventKeyMap?[key]
160175
}
161176

162177
/**
@@ -249,7 +264,14 @@ extension ProjectConfig {
249264
}
250265

251266
var allExperiments:[Experiment] {
252-
return project.experiments + project.groups.map({$0.experiments}).flatMap({$0})
267+
if let _allExperiments = _allExperiments {
268+
return _allExperiments
269+
}
270+
else {
271+
_allExperiments = project.experiments + project.groups.map({$0.experiments}).flatMap({$0})
272+
return _allExperiments ?? []
273+
}
274+
253275
}
254276

255277
}

OptimizelySDK/Optimizely/OptimizelyManager.swift

Lines changed: 82 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ open class OptimizelyManager: NSObject {
4444
}
4545

4646
private let reInitLock = Dispatch.DispatchSemaphore(value: 1)
47+
private let eventImitterQueue = DispatchQueue(label:"OptimizelyEventImitterQueue")
4748

4849
// MARK: - Public interfaces
4950

@@ -238,30 +239,11 @@ open class OptimizelyManager: NSObject {
238239

239240
let variation = try getVariation(experimentKey: experimentKey, userId: userId, attributes: attributes)
240241

241-
// TODO: fix to throw errors
242-
guard let body = BatchEventBuilder.createImpressionEvent(config: config,
243-
experiment: experiment,
244-
varionation: variation,
245-
userId: userId,
246-
attributes: attributes) else
247-
{
248-
throw OptimizelyError.eventBuildFailure(DispatchEvent.activateEventKey)
249-
}
250-
251-
let event = EventForDispatch(body: body)
252-
// because we are batching events, we cannot guarantee that the completion handler will be
253-
// called. So, for now, we are queuing and calling onActivate. Maybe we should mention that
254-
// onActivate only means the event has been queued and not necessarily sent.
255-
eventDispatcher.dispatchEvent(event: event) { result in
256-
switch result {
257-
case .failure:
258-
break
259-
case .success( _):
260-
break
261-
}
262-
}
242+
sendImpressionEvent(experiment: experiment,
243+
variation:variation,
244+
userId: userId,
245+
attributes: attributes)
263246

264-
self.notificationCenter.sendNotifications(type: NotificationType.Activate.rawValue, args: [experiment, userId, attributes, variation, ["url":event.url as Any, "body":event.body as Any]])
265247

266248
return variation.key
267249
}
@@ -412,31 +394,8 @@ open class OptimizelyManager: NSObject {
412394
sourceInfo[Constants.ExperimentDecisionInfoKeys.experiment] = experiment.key
413395
sourceInfo[Constants.ExperimentDecisionInfoKeys.variation] = variation.key
414396
decisionInfo[Constants.DecisionInfoKeys.sourceInfo] = sourceInfo
415-
416-
// TODO: fix to throw errors
417-
guard let body = BatchEventBuilder.createImpressionEvent(config: config,
418-
experiment: experiment,
419-
varionation: variation,
420-
userId: userId,
421-
attributes: attributes) else
422-
{
423-
throw OptimizelyError.eventBuildFailure(DispatchEvent.activateEventKey)
424-
}
425-
426-
let event = EventForDispatch(body: body)
427-
428-
// because we are batching events, we cannot guarantee that the completion handler will be
429-
// called. So, for now, we are queuing and calling onActivate. Maybe we should mention that
430-
// onActivate only means the event has been queued and not necessarily sent.
431-
eventDispatcher.dispatchEvent(event: event) { result in
432-
switch result {
433-
case .failure:
434-
break
435-
case .success(_):
436-
break
437-
}
438-
}
439-
self.notificationCenter.sendNotifications(type: NotificationType.Activate.rawValue, args: [experiment, userId, attributes, variation, ["url":event.url as Any, "body":event.body as Any]])
397+
398+
sendImpressionEvent(experiment: experiment, variation: variation, userId: userId, attributes: attributes)
440399
}
441400

442401
decisionInfo[Constants.DecisionInfoKeys.featureEnabled] = featureEnabled
@@ -664,32 +623,86 @@ open class OptimizelyManager: NSObject {
664623
throw OptimizelyError.eventKeyInvalid(eventKey)
665624
}
666625

667-
// TODO: fix to throw errors
668-
guard let body = BatchEventBuilder.createConversionEvent(config: config,
669-
eventKey: eventKey,
670-
userId: userId,
671-
attributes: attributes,
672-
eventTags: eventTags) else
673-
{
674-
throw OptimizelyError.eventBuildFailure(eventKey)
626+
sendConversionEvent(eventKey: eventKey, userId: userId, attributes: attributes, eventTags: eventTags)
627+
}
628+
629+
}
630+
631+
extension OptimizelyManager {
632+
633+
func sendImpressionEvent(experiment: Experiment,
634+
variation:Variation,
635+
userId: String,
636+
attributes: OptimizelyAttributes?=nil) {
637+
638+
eventImitterQueue.async {
639+
guard let config = self.config else { return }
640+
641+
guard let body = BatchEventBuilder.createImpressionEvent(config: config,
642+
experiment: experiment,
643+
varionation: variation,
644+
userId: userId,
645+
attributes: attributes) else
646+
{
647+
self.logger.e(OptimizelyError.eventBuildFailure(DispatchEvent.activateEventKey))
648+
return
649+
}
650+
651+
let event = EventForDispatch(body: body)
652+
// because we are batching events, we cannot guarantee that the completion handler will be
653+
// called. So, for now, we are queuing and calling onActivate. Maybe we should mention that
654+
// onActivate only means the event has been queued and not necessarily sent.
655+
self.eventDispatcher.dispatchEvent(event: event) { result in
656+
switch result {
657+
case .failure:
658+
break
659+
case .success( _):
660+
break
661+
}
662+
}
663+
664+
self.notificationCenter.sendNotifications(type: NotificationType.Activate.rawValue, args: [experiment, userId, attributes, variation, ["url":event.url as Any, "body":event.body as Any]])
665+
675666
}
667+
668+
}
669+
670+
func sendConversionEvent(eventKey: String,
671+
userId: String,
672+
attributes: OptimizelyAttributes?=nil,
673+
eventTags: OptimizelyEventTags?=nil) {
676674

677-
let event = EventForDispatch(body: body)
678-
// because we are batching events, we cannot guarantee that the completion handler will be
679-
// called. So, for now, we are queuing and calling onTrack. Maybe we should mention that
680-
// onTrack only means the event has been queued and not necessarily sent.
681-
eventDispatcher.dispatchEvent(event: event) { result in
682-
switch result {
683-
case .failure:
684-
break
685-
case .success( _):
686-
break
675+
676+
eventImitterQueue.async {
677+
guard let config = self.config else { return }
678+
679+
guard let body = BatchEventBuilder.createConversionEvent(config: config,
680+
eventKey: eventKey,
681+
userId: userId,
682+
attributes: attributes,
683+
eventTags: eventTags) else
684+
{
685+
self.logger.e(OptimizelyError.eventBuildFailure(eventKey))
686+
return
687+
}
688+
689+
let event = EventForDispatch(body: body)
690+
// because we are batching events, we cannot guarantee that the completion handler will be
691+
// called. So, for now, we are queuing and calling onTrack. Maybe we should mention that
692+
// onTrack only means the event has been queued and not necessarily sent.
693+
self.eventDispatcher.dispatchEvent(event: event) { result in
694+
switch result {
695+
case .failure:
696+
break
697+
case .success( _):
698+
break
699+
}
687700
}
701+
self.notificationCenter.sendNotifications(type: NotificationType.Track.rawValue, args: [eventKey, userId, attributes, eventTags, ["url":event.url as Any, "body":event.body as Any]])
702+
688703
}
689-
self.notificationCenter.sendNotifications(type: NotificationType.Track.rawValue, args: [eventKey, userId, attributes, eventTags, ["url":event.url as Any, "body":event.body as Any]])
690-
704+
691705
}
692-
693706
}
694707

695708
extension OptimizelyManager {

0 commit comments

Comments
 (0)