From 8cc67f7aeb9d89235b7e96f760d14b7a28e94cb8 Mon Sep 17 00:00:00 2001 From: artesmichael Date: Wed, 3 Dec 2025 13:00:30 +0100 Subject: [PATCH 1/6] 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/6] 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/6] 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/6] 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) } } From f627e1af7a819b0e477cbe378ead24772e7b3bcb Mon Sep 17 00:00:00 2001 From: artesmichael Date: Tue, 31 Mar 2026 11:42:49 +0200 Subject: [PATCH 5/6] Fixed build issues --- Tests/OptimizelyTests-Common/DataStoreTests.swift | 2 +- Tests/OptimizelyTests-Common/EventDispatcherTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/OptimizelyTests-Common/DataStoreTests.swift b/Tests/OptimizelyTests-Common/DataStoreTests.swift index ec58c44cf..808c9f3df 100644 --- a/Tests/OptimizelyTests-Common/DataStoreTests.swift +++ b/Tests/OptimizelyTests-Common/DataStoreTests.swift @@ -182,7 +182,7 @@ class DataStoreTests: XCTestCase { var url = try XCTUnwrap(FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first) .appendingPathComponent("testFileStore", isDirectory: false) - XCTAssertTrue(FileManager.default.fileExists(atPath: url.path())) + XCTAssertTrue(FileManager.default.fileExists(atPath: url.path)) } diff --git a/Tests/OptimizelyTests-Common/EventDispatcherTests.swift b/Tests/OptimizelyTests-Common/EventDispatcherTests.swift index f68ad16dd..cd47640ea 100644 --- a/Tests/OptimizelyTests-Common/EventDispatcherTests.swift +++ b/Tests/OptimizelyTests-Common/EventDispatcherTests.swift @@ -82,7 +82,7 @@ class EventDispatcherTests: XCTestCase { } func testEventDispatcherFile() { - eventDispatcher = DefaultEventDispatcher( backingStore: .file) + eventDispatcher = DefaultEventDispatcher( backingStore: .file(directory: nil)) let pEventD: OPTEventDispatcher = eventDispatcher! eventDispatcher?.timerInterval = 1 let wait = {() in From 66b3c8e153f655d6d6e0277e759d9b74322d1385 Mon Sep 17 00:00:00 2001 From: artesmichael Date: Tue, 31 Mar 2026 13:44:58 +0200 Subject: [PATCH 6/6] Fixed failing test --- Tests/OptimizelyTests-Common/DataStoreTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/OptimizelyTests-Common/DataStoreTests.swift b/Tests/OptimizelyTests-Common/DataStoreTests.swift index 808c9f3df..3c36e2058 100644 --- a/Tests/OptimizelyTests-Common/DataStoreTests.swift +++ b/Tests/OptimizelyTests-Common/DataStoreTests.swift @@ -174,7 +174,7 @@ class DataStoreTests: XCTestCase { func testFileStoreCustomDirectory() throws { createDirectoryIfNeeded(directory: .applicationSupportDirectory) - let datastore = DataStoreFile<[String]>(storeName: "testFileStore") + let datastore = DataStoreFile<[String]>(storeName: "testFileStore", directory: .applicationSupportDirectory) datastore.saveItem(forKey: "testString", value: ["value"]) let vj = datastore.getItem(forKey: "testString") as! [String]