Skip to content

Commit e0d87ac

Browse files
(chore):refactor data file handler to be more generic and allow for addition … (#186)
* refactor data file handler to be more generic and allow for addition of other types of data file handers (i.e. push notification, server side events, etc.) * make the datafilehandler interface accessable so to allow stopping of updates and general access to datafile handler services * add exposure to the endpoint per sdk key so that they can go to different cdns if necessary * update test * fix broken datafile manager * refactored out the fact that we don't set the timer if the interval is <= 0 * remove event test update
1 parent 9cd69b8 commit e0d87ac

File tree

7 files changed

+90
-40
lines changed

7 files changed

+90
-40
lines changed

DemoSwiftApp/AppDelegate.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
5555

5656
func initializeOptimizelySDKAsynchronous() {
5757
optimizely = OptimizelyClient(sdkKey: sdkKey)
58+
var handler = optimizely.datafileHandler
59+
handler.endPointStringFormat = "https://cdn.optimizely.com/datafiles/%@.json"
5860

5961
optimizely.start { result in
6062
switch result {

OptimizelySDK/Extensions/OptimizelyClient+Extension.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,32 @@ extension OptimizelyClient {
4242
// sdk keys without having to be created for every key.
4343
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))
4444
}
45+
46+
/// Optimizely Manager
47+
///
48+
/// - Parameters:
49+
/// - sdkKey: sdk key
50+
/// - logger: custom Logger
51+
/// - eventDispatcher: custom EventDispatcher (optional)
52+
/// - userProfileService: custom UserProfileService (optional)
53+
/// - periodicDownloadInterval: custom interval for periodic background datafile download (optional. default = 10 * 60 secs)
54+
/// - defaultLogLevel: default log level (optional. default = .info)
55+
public convenience init(sdkKey: String,
56+
logger: OPTLogger? = nil,
57+
eventDispatcher: OPTEventDispatcher? = nil,
58+
userProfileService: OPTUserProfileService? = nil,
59+
periodicDownloadInterval:Int? = nil,
60+
defaultLogLevel: OptimizelyLogLevel? = nil) {
61+
let interval = periodicDownloadInterval ?? 10 * 60
62+
63+
64+
self.init(sdkKey: sdkKey, logger: logger, eventDispatcher: eventDispatcher, userProfileService: userProfileService, defaultLogLevel: defaultLogLevel)
65+
66+
if let handler = datafileHandler as? DefaultDatafileHandler, interval > 0 {
67+
handler.setTimer(sdkKey: sdkKey, interval: interval)
68+
}
69+
70+
71+
}
72+
4573
}

OptimizelySDK/Implementation/DefaultDatafileHandler.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import Foundation
1818

1919
class DefaultDatafileHandler : OPTDatafileHandler {
20-
static public var endPointStringFormat = "https://cdn.optimizely.com/datafiles/%@.json"
20+
public var endPointStringFormat = "https://cdn.optimizely.com/datafiles/%@.json"
2121
lazy var logger = HandlerRegistryService.shared.injectLogger()
22-
var timers:AtomicProperty<[String:(timer:Timer, interval:Int)]> = AtomicProperty(property: [String:(Timer,Int)]())
22+
var timers:AtomicProperty<[String:(timer:Timer?, interval:Int)]> = AtomicProperty(property: [String:(Timer?,Int)]())
2323
let dataStore = DataStoreUserDefaults()
2424

2525
let downloadQueue = DispatchQueue(label: "DefaultDatafileHandlerQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
@@ -28,6 +28,15 @@ class DefaultDatafileHandler : OPTDatafileHandler {
2828

2929
}
3030

31+
func setTimer(sdkKey:String, interval:Int) {
32+
timers.performAtomic { (timers) in
33+
guard let _ = timers[sdkKey] else {
34+
timers[sdkKey] = (nil, interval)
35+
return
36+
}
37+
}
38+
}
39+
3140
func downloadDatafile(sdkKey: String) -> Data? {
3241

3342
var datafile:Data?
@@ -60,7 +69,7 @@ class DefaultDatafileHandler : OPTDatafileHandler {
6069
}
6170

6271
open func getRequest(sdkKey:String) -> URLRequest? {
63-
let str = String(format: DefaultDatafileHandler.endPointStringFormat, sdkKey)
72+
let str = String(format: endPointStringFormat, sdkKey)
6473
guard let url = URL(string: str) else { return nil }
6574

6675
var request = URLRequest(url: url)
@@ -231,20 +240,36 @@ class DefaultDatafileHandler : OPTDatafileHandler {
231240
if let timer = timers[sdkKey] {
232241
logger?.i("Stopping timer for datafile updates sdkKey: \(sdkKey)")
233242

234-
timer.timer.invalidate()
243+
timer.timer?.invalidate()
235244
timers.removeValue(forKey: sdkKey)
236245
}
237246

238247
}
239248
}
240249

241250
func stopPeriodicUpdates() {
242-
for key in timers.property?.keys ?? Dictionary<String, (timer: Timer, interval: Int)>().keys {
251+
for key in timers.property?.keys ?? Dictionary<String, (timer: Timer?, interval: Int)>().keys {
243252
logger?.i("Stopping timer for all datafile updates")
244253
stopPeriodicUpdates(sdkKey: key)
245254
}
246255

247256
}
257+
258+
func startUpdates(sdkKey: String, datafileChangeNotification: ((Data) -> Void)?) {
259+
if let value = timers.property?[sdkKey], !(value.timer?.isValid ?? false) {
260+
startPeriodicUpdates(sdkKey: sdkKey, updateInterval: value.interval, datafileChangeNotification: datafileChangeNotification)
261+
}
262+
}
263+
264+
func stopUpdates(sdkKey: String) {
265+
stopPeriodicUpdates(sdkKey: sdkKey)
266+
}
267+
268+
func stopAllUpdates() {
269+
stopPeriodicUpdates()
270+
}
271+
272+
248273

249274

250275
func saveDatafile(sdkKey: String, dataFile: Data) {

OptimizelySDK/Optimizely/OptimizelyClient.swift

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ open class OptimizelyClient: NSObject {
3434
var eventDispatcher: OPTEventDispatcher {
3535
return HandlerRegistryService.shared.injectEventDispatcher(sdkKey: self.sdkKey)!
3636
}
37-
let periodicDownloadInterval: Int
3837

3938
// MARK: - Default Services
4039

@@ -43,7 +42,8 @@ open class OptimizelyClient: NSObject {
4342
var decisionService: OPTDecisionService {
4443
return HandlerRegistryService.shared.injectDecisionService(sdkKey: self.sdkKey)!
4544
}
46-
var datafileHandler: OPTDatafileHandler {
45+
46+
public var datafileHandler: OPTDatafileHandler {
4747
return HandlerRegistryService.shared.injectDatafileHandler(sdkKey: self.sdkKey)!
4848
}
4949

@@ -68,11 +68,9 @@ open class OptimizelyClient: NSObject {
6868
logger: OPTLogger? = nil,
6969
eventDispatcher: OPTEventDispatcher? = nil,
7070
userProfileService: OPTUserProfileService? = nil,
71-
periodicDownloadInterval: Int? = nil,
7271
defaultLogLevel: OptimizelyLogLevel? = nil) {
7372

7473
self.sdkKey = sdkKey
75-
self.periodicDownloadInterval = periodicDownloadInterval ?? 10 * 60
7674

7775
super.init()
7876

@@ -158,34 +156,30 @@ open class OptimizelyClient: NSObject {
158156
// this isn't really necessary because the try would throw if there is a problem. But, we want to avoid using bang so we do another let binding.
159157
guard let config = self.config else { throw OptimizelyError.dataFileInvalid }
160158

161-
if periodicDownloadInterval > 0 {
162-
datafileHandler.stopPeriodicUpdates(sdkKey: self.sdkKey)
163-
datafileHandler.startPeriodicUpdates(sdkKey: self.sdkKey, updateInterval: periodicDownloadInterval) { data in
164-
// new datafile came in...
165-
self.reInitLock.wait(); defer { self.reInitLock.signal() }
166-
if let config = try? ProjectConfig(datafile: data) {
167-
do {
168-
if let users = self.config?.whitelistUsers {
169-
config.whitelistUsers = users
170-
}
171-
172-
self.config = config
173-
174-
// call reinit on the services we know we are reinitializing.
175-
176-
for component in HandlerRegistryService.shared.lookupComponents(sdkKey: self.sdkKey) ?? [] {
177-
guard let component = component else { continue }
178-
HandlerRegistryService.shared.reInitializeComponent(service: component, sdkKey: self.sdkKey)
179-
}
180-
159+
datafileHandler.startUpdates(sdkKey: self.sdkKey) { data in
160+
// new datafile came in...
161+
self.reInitLock.wait(); defer { self.reInitLock.signal() }
162+
if let config = try? ProjectConfig(datafile: data) {
163+
do {
164+
if let users = self.config?.whitelistUsers {
165+
config.whitelistUsers = users
181166
}
182167

183-
self.notificationCenter.sendNotifications(type:
184-
NotificationType.DatafileChange.rawValue, args: [data])
168+
self.config = config
169+
170+
// call reinit on the services we know we are reinitializing.
171+
172+
for component in HandlerRegistryService.shared.lookupComponents(sdkKey: self.sdkKey) ?? [] {
173+
guard let component = component else { continue }
174+
HandlerRegistryService.shared.reInitializeComponent(service: component, sdkKey: self.sdkKey)
175+
}
185176

186177
}
178+
179+
self.notificationCenter.sendNotifications(type:
180+
NotificationType.DatafileChange.rawValue, args: [data])
181+
187182
}
188-
189183
}
190184
} catch let error as OptimizelyError {
191185
// .datafileInvalid

OptimizelySDK/OptimizelyTests/OptimizelyTests-Common/DatafileHandlerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class DatafileHandlerTests: XCTestCase {
125125
let _ = optimizely.notificationCenter.addDatafileChangeNotificationListener { (data) in
126126
count += 1
127127
if count == 9 {
128-
optimizely.datafileHandler.stopPeriodicUpdates()
128+
optimizely.datafileHandler.stopAllUpdates()
129129
expection.fulfill()
130130
}
131131
}

OptimizelySDK/OptimizelyTests/OptimizelyTests-Common/DecisionListenerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,9 @@ class FakeManager: OptimizelyClient {
571571
}
572572
}
573573

574-
override init(sdkKey: String, logger:OPTLogger? = nil, eventDispatcher:OPTEventDispatcher? = nil, userProfileService:OPTUserProfileService? = nil, periodicDownloadInterval:Int? = nil, defaultLogLevel: OptimizelyLogLevel? = nil) {
574+
override init(sdkKey: String, logger:OPTLogger? = nil, eventDispatcher:OPTEventDispatcher? = nil, userProfileService:OPTUserProfileService? = nil, defaultLogLevel: OptimizelyLogLevel? = nil) {
575575

576-
super.init(sdkKey: sdkKey, logger: logger, eventDispatcher: eventDispatcher, userProfileService: userProfileService, periodicDownloadInterval: periodicDownloadInterval, defaultLogLevel: defaultLogLevel)
576+
super.init(sdkKey: sdkKey, logger: logger, eventDispatcher: eventDispatcher, userProfileService: userProfileService, defaultLogLevel: defaultLogLevel)
577577
HandlerRegistryService.shared.removeAll()
578578

579579
let userProfileService = userProfileService ?? DefaultUserProfileService()

OptimizelySDK/Protocols/OPTDatafileHandler.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public typealias DatafileDownloadCompletionHandler = (OptimizelyResult<Data?>) -
2323
///
2424
public protocol OPTDatafileHandler {
2525
init()
26+
27+
var endPointStringFormat:String { get set }
2628
/**
2729
Synchronous call to download the datafile.
2830

@@ -43,24 +45,23 @@ public protocol OPTDatafileHandler {
4345
completionHandler:@escaping DatafileDownloadCompletionHandler)
4446

4547
/**
46-
Start periodic updates to the project datafile .
48+
Start updates to the project datafile .
4749

4850
- Parameter sdkKey: SdkKey for the datafile
49-
- Parameter updateInterval: frequency of updates in seconds
5051
*/
51-
func startPeriodicUpdates(sdkKey:String, updateInterval:Int, datafileChangeNotification:((Data)->Void)?)
52+
func startUpdates(sdkKey:String, datafileChangeNotification:((Data)->Void)?)
5253

5354
/**
5455
Stop the periodic updates. This should be called when the app goes to background
5556

5657
- Parameter sdkKey: sdk key for datafile.
5758
*/
58-
func stopPeriodicUpdates(sdkKey:String)
59+
func stopUpdates(sdkKey:String)
5960

6061
/**
6162
Stop all periodic updates. This should be called when the app goes to background
6263
*/
63-
func stopPeriodicUpdates()
64+
func stopAllUpdates()
6465

6566
/**
6667
Save the datafile to cache.

0 commit comments

Comments
 (0)