From 8cc67f7aeb9d89235b7e96f760d14b7a28e94cb8 Mon Sep 17 00:00:00 2001 From: artesmichael Date: Wed, 3 Dec 2025 13:00:30 +0100 Subject: [PATCH 1/4] refactor: Added optional directory parameter to DataStoreFile --- .../Datastore/DataStoreFile.swift | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Sources/Implementation/Datastore/DataStoreFile.swift b/Sources/Implementation/Datastore/DataStoreFile.swift index 2ad28c628..027786d01 100644 --- a/Sources/Implementation/Datastore/DataStoreFile.swift +++ b/Sources/Implementation/Datastore/DataStoreFile.swift @@ -30,21 +30,27 @@ open class DataStoreFile: OPTDataStore where T: Codable { return threadSafeLogger.logger } - public init(storeName: String, async: Bool = true) { - self.async = async - dataStoreName = storeName - lock = DispatchQueue(label: storeName) + public convenience init(storeName: String, async: Bool = true) { #if os(tvOS) || os(macOS) let directory = FileManager.SearchPathDirectory.cachesDirectory #else let directory = FileManager.SearchPathDirectory.documentDirectory #endif - if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first { - self.url = url.appendingPathComponent(storeName, isDirectory: false) + + self.init(storeName: storeName, directory: directory, async: async) + } + + public init(storeName: String, directory: FileManager.SearchPathDirectory, async: Bool = true) { + self.async = async + + dataStoreName = storeName + lock = DispatchQueue(label: storeName) + + url = if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first { + url.appendingPathComponent(storeName, isDirectory: false) } else { - self.url = URL(fileURLWithPath: storeName) + URL(fileURLWithPath: storeName) } - } func isArray() -> Bool { From 97fe98922c1eca312ec021049a872a6b2c620695 Mon Sep 17 00:00:00 2001 From: artesmichael Date: Wed, 3 Dec 2025 13:08:27 +0100 Subject: [PATCH 2/4] refactor: Added ability to inject file data store directory into DefaultEventDispatcher --- .../Customization/DefaultEventDispatcher.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/Customization/DefaultEventDispatcher.swift b/Sources/Customization/DefaultEventDispatcher.swift index 2852d259a..c7b95388e 100644 --- a/Sources/Customization/DefaultEventDispatcher.swift +++ b/Sources/Customization/DefaultEventDispatcher.swift @@ -17,7 +17,7 @@ import Foundation public enum DataStoreType { - case file, memory, userDefaults + case file(directory: FileManager.SearchPathDirectory? = nil), memory, userDefaults } open class DefaultEventDispatcher: BackgroundingCallbacks, OPTEventDispatcher { @@ -54,7 +54,7 @@ open class DefaultEventDispatcher: BackgroundingCallbacks, OPTEventDispatcher { let reachability = NetworkReachability(maxContiguousFails: 1) public init(batchSize: Int = DefaultValues.batchSize, - backingStore: DataStoreType = .file, + backingStore: DataStoreType = .file(directory: nil), dataStoreName: String = "OPTEventQueue", timerInterval: TimeInterval = DefaultValues.timeInterval, maxQueueSize: Int = DefaultValues.maxQueueSize) { @@ -63,9 +63,14 @@ open class DefaultEventDispatcher: BackgroundingCallbacks, OPTEventDispatcher { self.maxQueueSize = maxQueueSize >= 100 ? maxQueueSize : DefaultValues.maxQueueSize switch backingStore { - case .file: - self.eventQueue = DataStoreQueueStackImpl(queueStackName: "OPTEventQueue", - dataStore: DataStoreFile<[Data]>(storeName: dataStoreName)) + case .file(let directory): + if let directory { + self.eventQueue = DataStoreQueueStackImpl(queueStackName: "OPTEventQueue", + dataStore: DataStoreFile<[Data]>(storeName: dataStoreName, directory: directory)) + } else { + self.eventQueue = DataStoreQueueStackImpl(queueStackName: "OPTEventQueue", + dataStore: DataStoreFile<[Data]>(storeName: dataStoreName)) + } case .memory: self.eventQueue = DataStoreQueueStackImpl(queueStackName: "OPTEventQueue", dataStore: DataStoreMemory<[Data]>(storeName: dataStoreName)) From 5feda76f35b063c22f215e491079fa8ec672d385 Mon Sep 17 00:00:00 2001 From: artesmichael Date: Wed, 3 Dec 2025 13:17:51 +0100 Subject: [PATCH 3/4] test: Added test for DataStoreFile custom directory --- .../DataStoreTests.swift | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Tests/OptimizelyTests-Common/DataStoreTests.swift b/Tests/OptimizelyTests-Common/DataStoreTests.swift index 05003b7d2..ec58c44cf 100644 --- a/Tests/OptimizelyTests-Common/DataStoreTests.swift +++ b/Tests/OptimizelyTests-Common/DataStoreTests.swift @@ -17,10 +17,9 @@ import XCTest class DataStoreTests: XCTestCase { - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { + + private func createDirectoryIfNeeded(directory: FileManager.SearchPathDirectory) { + if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first { if (!FileManager.default.fileExists(atPath: url.path)) { do { try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil) @@ -30,7 +29,11 @@ class DataStoreTests: XCTestCase { } } + } + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + createDirectoryIfNeeded(directory: .documentDirectory) } override func tearDown() { @@ -168,6 +171,19 @@ class DataStoreTests: XCTestCase { datastore.removeItem(forKey: key) } + func testFileStoreCustomDirectory() throws { + createDirectoryIfNeeded(directory: .applicationSupportDirectory) + + let datastore = DataStoreFile<[String]>(storeName: "testFileStore") + + datastore.saveItem(forKey: "testString", value: ["value"]) + let vj = datastore.getItem(forKey: "testString") as! [String] + XCTAssertEqual(vj.first, "value") + + var url = try XCTUnwrap(FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first) + .appendingPathComponent("testFileStore", isDirectory: false) + XCTAssertTrue(FileManager.default.fileExists(atPath: url.path())) + } func testUserDefaults() { From 16bfad1341783cc7561fd5d56289a9fe675a7e68 Mon Sep 17 00:00:00 2001 From: artesmichael Date: Thu, 4 Dec 2025 10:58:10 +0100 Subject: [PATCH 4/4] refactor: Updated to syntax supported by Swift 5.8 and lower --- Sources/Implementation/Datastore/DataStoreFile.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Implementation/Datastore/DataStoreFile.swift b/Sources/Implementation/Datastore/DataStoreFile.swift index 027786d01..51c92e310 100644 --- a/Sources/Implementation/Datastore/DataStoreFile.swift +++ b/Sources/Implementation/Datastore/DataStoreFile.swift @@ -46,10 +46,10 @@ open class DataStoreFile: OPTDataStore where T: Codable { dataStoreName = storeName lock = DispatchQueue(label: storeName) - url = if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first { - url.appendingPathComponent(storeName, isDirectory: false) + if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first { + self.url = url.appendingPathComponent(storeName, isDirectory: false) } else { - URL(fileURLWithPath: storeName) + self.url = URL(fileURLWithPath: storeName) } }