Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
fa69963
1. remove all print statements. 2. datastore memory backing store use…
thomaszurkan-optimizely Mar 20, 2020
a5da81e
default to in memory for event dispatcher
thomaszurkan-optimizely Mar 20, 2020
37ac9df
update to put a capacity check on user defaults size
thomaszurkan-optimizely Mar 24, 2020
aa9582a
Update Tests/OptimizelyTests-Common/DataStoreTests.swift
thomaszurkan-optimizely Mar 24, 2020
ae58bc0
fix datafile handler as well
thomaszurkan-optimizely Mar 24, 2020
e9ca1f5
Merge branch 'cleanUp' of https://github.com/optimizely/swift-sdk int…
thomaszurkan-optimizely Mar 24, 2020
db33f83
cleanup
thomaszurkan-optimizely Mar 24, 2020
b37a6ca
datastore memory should remove from backing store.
thomaszurkan-optimizely Mar 24, 2020
d1ed32f
moved removeItem to OPTDataStore. Fixed tests.
thomaszurkan-optimizely Mar 25, 2020
63f34c9
clear out the registry before and after
thomaszurkan-optimizely Mar 25, 2020
4f7bd81
make sure the cache files have been removed before starting tests
thomaszurkan-optimizely Mar 25, 2020
963e9d5
use datastore but make sure that it is synched when done
thomaszurkan-optimizely Mar 25, 2020
e284c27
a little cleanup
thomaszurkan-optimizely Mar 25, 2020
683f646
fix key
thomaszurkan-optimizely Mar 25, 2020
c2715d7
cleanup the errors
thomaszurkan-optimizely Mar 25, 2020
fd38ccf
fix so that data files still work
thomaszurkan-optimizely Mar 25, 2020
acc7814
remove compiler warnings
thomaszurkan-optimizely Mar 26, 2020
081d536
fix swiftlint warnings
jaeopt Mar 26, 2020
7d0eb50
add tests for datafile cache and event queue format compatibility
jaeopt Mar 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion OptimizelySwiftSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,7 @@
6E14CD642423F80B00010234 /* OptimizelyTests-Batch-iOS */ = {
isa = PBXGroup;
children = (
6E75198D22C5211100B2B157 /* EventDispatcherTests_Batch.swift */,
);
path = "OptimizelyTests-Batch-iOS";
sourceTree = "<group>";
Expand Down Expand Up @@ -1945,7 +1946,6 @@
6E75198A22C5211100B2B157 /* BucketTests_Base.swift */,
6E75198B22C5211100B2B157 /* NotificationCenterTests.swift */,
6E75198C22C5211100B2B157 /* BucketTests_ExpToVariation.swift */,
6E75198D22C5211100B2B157 /* EventDispatcherTests_Batch.swift */,
6E75198E22C5211100B2B157 /* LoggerTests.swift */,
6E75198F22C5211100B2B157 /* BucketTests_BucketVariation.swift */,
6E75199022C5211100B2B157 /* DecisionListenerTests.swift */,
Expand Down
8 changes: 7 additions & 1 deletion Sources/Customization/DefaultEventDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ public enum DataStoreType {

open class DefaultEventDispatcher: BackgroundingCallbacks, OPTEventDispatcher {

static let sharedInstance = DefaultEventDispatcher()
#if os(tvOS)
static let sharedInstance =
DefaultEventDispatcher(backingStore: .memory)
#else
static let sharedInstance =
DefaultEventDispatcher()
#endif

// timer-interval for batching (0 = no batching, negative = use default)
var timerInterval: TimeInterval
Expand Down
72 changes: 52 additions & 20 deletions Sources/Implementation/Datastore/DataStoreFile.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2019, Optimizely, Inc. and contributors *
* Copyright 2019-2020, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand All @@ -21,21 +21,16 @@ import Foundation
public class DataStoreFile<T>: OPTDataStore where T: Codable {
let dataStoreName: String
let lock: DispatchQueue
let url: URL
let async: Bool
public let url: URL
lazy var logger: OPTLogger? = OPTLoggerFactory.getLogger()

init(storeName: String) {
init(storeName: String, async: Bool = true) {
self.async = async
dataStoreName = storeName
lock = DispatchQueue(label: storeName)
if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
self.url = url.appendingPathComponent(storeName, isDirectory: false)
if !FileManager.default.fileExists(atPath: self.url.path) {
do {
let data = try JSONEncoder().encode([Data]())
try data.write(to: self.url, options: .atomicWrite)
} catch let error {
print(error.localizedDescription)
}
}
} else {
self.url = URL(fileURLWithPath: storeName)
}
Expand All @@ -47,26 +42,63 @@ public class DataStoreFile<T>: OPTDataStore where T: Codable {
lock.sync {
do {
let contents = try Data(contentsOf: self.url)
let item = try JSONDecoder().decode(T.self, from: contents)
returnItem = item
} catch let errorr {
print(errorr.localizedDescription)
if type(of: T.self) == type(of: Data.self) {
returnItem = contents as? T
} else {
let item = try JSONDecoder().decode(T.self, from: contents)
returnItem = item
}
} catch let e as NSError {
if e.code != 260 {
self.logger?.e(e.localizedDescription)
}
}
}

return returnItem
}

func doCall(async: Bool, block:@escaping () -> Void) {
if async {
lock.async {
block()
}
} else {
lock.sync {
block()
}
}
}

public func saveItem(forKey: String, value: Any) {
lock.async {
doCall(async: self.async) {
do {
if let value = value as? T {
let data = try JSONEncoder().encode(value)
try data.write(to: self.url, options: .atomic)
var data: Data?
// don't bother to convert... otherwise, do
if let value = value as? Data {
data = value
} else {
data = try JSONEncoder().encode(value)
}
if let data = data {
try data.write(to: self.url, options: .atomic)
}
}
} catch let error {
print(error.localizedDescription)
} catch let e {
self.logger?.e(e.localizedDescription)
}
}
}

public func removeItem(forKey: String) {
doCall(async: self.async) {
do {
try FileManager.default.removeItem(at: self.url)
} catch let e {
self.logger?.e(e.localizedDescription)
}
}

}
}
58 changes: 24 additions & 34 deletions Sources/Implementation/Datastore/DataStoreMemory.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2019, Optimizely, Inc. and contributors *
* Copyright 2019-2020, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand All @@ -22,26 +22,15 @@ import Foundation
public class DataStoreMemory<T>: BackgroundingCallbacks, OPTDataStore where T: Codable {
let dataStoreName: String
let lock: DispatchQueue
let url: URL
var data: T?
var backupDataStore: OPTDataStore
lazy var logger: OPTLogger? = OPTLoggerFactory.getLogger()

init(storeName: String) {
init(storeName: String, backupStore: OPTDataStore = DataStoreUserDefaults()) {
dataStoreName = storeName
lock = DispatchQueue(label: storeName)
if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
self.url = url.appendingPathComponent(storeName, isDirectory: false)
if !FileManager.default.fileExists(atPath: self.url.path) {
do {
let data = try JSONEncoder().encode([Data]())
try data.write(to: self.url, options: .atomicWrite)
} catch let error {
print(error.localizedDescription)
}
}
} else {
self.url = URL(fileURLWithPath: storeName)
}

backupDataStore = backupStore
load(forKey: dataStoreName)
subscribe()
}

Expand All @@ -50,22 +39,22 @@ public class DataStoreMemory<T>: BackgroundingCallbacks, OPTDataStore where T: C
}

public func getItem(forKey: String) -> Any? {
var returnData: T?

var retVal: T?
lock.sync {
returnData = data
retVal = self.data
}
return returnData
return retVal
}

public func load(forKey: String) {
lock.sync {
do {
let contents = try Data(contentsOf: self.url)
let item = try JSONDecoder().decode(T.self, from: contents)
self.data = item
} catch let errorr {
print(errorr.localizedDescription)
if let contents = backupDataStore.getItem(forKey: dataStoreName) as? Data {
let item = try JSONDecoder().decode(T.self, from: contents)
self.data = item
}
} catch let error {
self.logger?.e(error.localizedDescription)
}
}
}
Expand All @@ -77,17 +66,18 @@ public class DataStoreMemory<T>: BackgroundingCallbacks, OPTDataStore where T: C
}
}
}

public func removeItem(forKey: String) {
lock.async {
self.data = nil
self.backupDataStore.removeItem(forKey: forKey)
}
// this is a no op here. data could be niled out.
}

func save(forKey: String, value: Any) {
lock.async {
do {
if let value = value as? T {
let data = try JSONEncoder().encode(value)
try data.write(to: self.url, options: .atomic)
}
} catch let error {
print(error.localizedDescription)
}
self.backupDataStore.saveItem(forKey: forKey, value: value)
}
}

Expand Down
43 changes: 42 additions & 1 deletion Sources/Implementation/Datastore/DataStoreUserDefaults.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2019, Optimizely, Inc. and contributors *
* Copyright 2019-2020, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand All @@ -19,7 +19,14 @@ import Foundation
/// Implementation of OPTDataStore using standard UserDefaults.
/// This class should be used as a singleton.
public class DataStoreUserDefaults: OPTDataStore {
// A hardcoded max for user defaults. Since there is a max on iostv
#if os(tvOS)
static let MAX_DS_SIZE = 128000
#else
static let MAX_DS_SIZE = 1000000
#endif
static let dispatchQueue = DispatchQueue(label: "OPTDataStoreQueueUserDefaults")
lazy var logger: OPTLogger = OPTLoggerFactory.getLogger()

public func getItem(forKey: String) -> Any? {

Expand All @@ -29,10 +36,44 @@ public class DataStoreUserDefaults: OPTDataStore {
}

public func saveItem(forKey: String, value: Any) {

DataStoreUserDefaults.dispatchQueue.async {
if let value = value as? Data {
if value.count > DataStoreUserDefaults.MAX_DS_SIZE {
self.logger.e("Save to User Defaults error: \(forKey) is too big to save size(\(value.count))")
return
}
} else if let value = value as? String {
if value.count > DataStoreUserDefaults.MAX_DS_SIZE {
self.logger.e("Save to User Defaults error: \(forKey) is too big to save size(\(value.count))")
return
}
} else if let value = value as? [Data] {
var l: Int = 0
l = value.reduce(into: l, { (res, data) in
res += data.count
})
if l > DataStoreUserDefaults.MAX_DS_SIZE {
self.logger.e("Save to User Defaults error: \(forKey) is too big to save size(\(value.count))")
return
}
} else if let value = value as? [String] {
var l: Int = 0
l = value.reduce(into: l, { (res, data) in
res += data.count
})
if l > DataStoreUserDefaults.MAX_DS_SIZE {
self.logger.e("Save to User Defaults error: \(forKey) is too big to save size(\(value.count))")
return
}
}
UserDefaults.standard.set(value, forKey: forKey)
UserDefaults.standard.synchronize()
}
}

public func removeItem(forKey: String) {
UserDefaults.standard.removeObject(forKey: forKey)
}

}
Loading