From 722749244c3dfd7a27de517e56bb52cf60f0f403 Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Wed, 29 May 2024 11:10:13 -0600 Subject: [PATCH 1/3] Use URL filename as cache key --- .../PathConfigurationLoader.swift | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Source/Path Configuration/PathConfigurationLoader.swift b/Source/Path Configuration/PathConfigurationLoader.swift index 3774525..06d671c 100644 --- a/Source/Path Configuration/PathConfigurationLoader.swift +++ b/Source/Path Configuration/PathConfigurationLoader.swift @@ -4,7 +4,6 @@ typealias PathConfigurationLoaderCompletionHandler = (PathConfigurationDecoder) final class PathConfigurationLoader { private let cacheDirectory = "Turbo" - private let configurationCacheFilename = "path-configuration.json" private let sources: [PathConfiguration.Source] private let options: PathConfigurationLoaderOptions? private var completionHandler: PathConfigurationLoaderCompletionHandler? @@ -20,7 +19,7 @@ final class PathConfigurationLoader { for source in sources { switch source { case .data(let data): - loadData(data) + loadData(data, for: .PathDataTemporaryURL) case .file(let url): loadFile(url) case .server(let url): @@ -35,8 +34,8 @@ final class PathConfigurationLoader { precondition(!url.isFileURL, "URL provided for server is a file url") // Immediately load most recent cached version if available - if let data = cachedData() { - loadData(data) + if let data = cachedData(for: url) { + loadData(data, for: url) } let session = options?.urlSessionConfiguration.map { URLSession(configuration: $0) } ?? URLSession.shared @@ -50,29 +49,31 @@ final class PathConfigurationLoader { return } - self?.loadData(data, cache: true) + self?.loadData(data, cache: true, for: url) }.resume() } // MARK: - Caching - private func cacheRemoteData(_ data: Data) { + private func cacheRemoteData(_ data: Data, for url: URL) { createCacheDirectoryIfNeeded() do { - try data.write(to: configurationCacheURL) + let url = configurationCacheURL(for: url) + try data.write(to: url) } catch { debugPrint("[path-configuration-loader] error caching file error: \(error)") } } - private func cachedData() -> Data? { - guard FileManager.default.fileExists(atPath: configurationCacheURL.path) else { + private func cachedData(for url: URL) -> Data? { + let cachedURL = configurationCacheURL(for: url) + guard FileManager.default.fileExists(atPath: cachedURL.path) else { return nil } do { - return try Data(contentsOf: configurationCacheURL) + return try Data(contentsOf: cachedURL) } catch { debugPrint("[path-configuration-loader] *** error loading cached data: \(error)") return nil @@ -94,8 +95,8 @@ final class PathConfigurationLoader { return directory.appendingPathComponent(cacheDirectory) } - var configurationCacheURL: URL { - turboCacheDirectoryURL.appendingPathComponent(configurationCacheFilename) + func configurationCacheURL(for url: URL) -> URL { + turboCacheDirectoryURL.appendingPathComponent(url.lastPathComponent) } // MARK: - File @@ -105,7 +106,7 @@ final class PathConfigurationLoader { do { let data = try Data(contentsOf: url) - loadData(data) + loadData(data, for: url) } catch { debugPrint("[path-configuration] *** error loading configuration from file: \(url), error: \(error)") } @@ -113,7 +114,7 @@ final class PathConfigurationLoader { // MARK: - Data - private func loadData(_ data: Data, cache: Bool = false) { + private func loadData(_ data: Data, cache: Bool = false, for url: URL) { do { guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { throw JSONDecodingError.invalidJSON @@ -123,7 +124,7 @@ final class PathConfigurationLoader { if cache { // Only cache once we ensure we have valid data - cacheRemoteData(data) + cacheRemoteData(data, for: url) } updateHandler(with: config) @@ -144,3 +145,7 @@ final class PathConfigurationLoader { } } } + +private extension URL { + static let PathDataTemporaryURL = URL(string: "https://localhost/path-configuration.json")! +} From 7a5915589d51e91778d13ecabcef8e1f5cf39303 Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Wed, 29 May 2024 20:46:50 -0600 Subject: [PATCH 2/3] Update unit tests --- Tests/PathConfigurationLoaderTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/PathConfigurationLoaderTests.swift b/Tests/PathConfigurationLoaderTests.swift index ffa1148..40b7948 100644 --- a/Tests/PathConfigurationLoaderTests.swift +++ b/Tests/PathConfigurationLoaderTests.swift @@ -55,7 +55,7 @@ class PathConfigurationLoaderTests: XCTestCase { wait(for: [expectation]) XCTAssertTrue(handlerCalled) - XCTAssertTrue(FileManager.default.fileExists(atPath: loader.configurationCacheURL.path)) + XCTAssertTrue(FileManager.default.fileExists(atPath: loader.configurationCacheURL(for: serverURL).path)) } private func stubRequest(for loader: PathConfigurationLoader) -> XCTestExpectation { @@ -64,7 +64,7 @@ class PathConfigurationLoaderTests: XCTestCase { return HTTPStubsResponse(jsonObject: json, statusCode: 200, headers: [:]) } - clearCache(loader.configurationCacheURL) + clearCache(loader.configurationCacheURL(for: serverURL)) return expectation(description: "Wait for configuration to load.") } From 30d0903a993e2fc49763515715a8624bccbda2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20=C5=A0vara?= Date: Thu, 30 May 2024 16:09:25 +0200 Subject: [PATCH 3/3] Remove the need to provide a url when loading path config's data. --- .../PathConfigurationLoader.swift | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Source/Path Configuration/PathConfigurationLoader.swift b/Source/Path Configuration/PathConfigurationLoader.swift index 06d671c..09b5438 100644 --- a/Source/Path Configuration/PathConfigurationLoader.swift +++ b/Source/Path Configuration/PathConfigurationLoader.swift @@ -19,7 +19,7 @@ final class PathConfigurationLoader { for source in sources { switch source { case .data(let data): - loadData(data, for: .PathDataTemporaryURL) + loadData(data) case .file(let url): loadFile(url) case .server(let url): @@ -35,7 +35,7 @@ final class PathConfigurationLoader { // Immediately load most recent cached version if available if let data = cachedData(for: url) { - loadData(data, for: url) + loadData(data) } let session = options?.urlSessionConfiguration.map { URLSession(configuration: $0) } ?? URLSession.shared @@ -106,7 +106,7 @@ final class PathConfigurationLoader { do { let data = try Data(contentsOf: url) - loadData(data, for: url) + loadData(data) } catch { debugPrint("[path-configuration] *** error loading configuration from file: \(url), error: \(error)") } @@ -114,7 +114,7 @@ final class PathConfigurationLoader { // MARK: - Data - private func loadData(_ data: Data, cache: Bool = false, for url: URL) { + private func loadData(_ data: Data, cache: Bool = false, for url: URL? = nil) { do { guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { throw JSONDecodingError.invalidJSON @@ -122,7 +122,7 @@ final class PathConfigurationLoader { let config = try PathConfigurationDecoder(json: json) - if cache { + if cache, let url { // Only cache once we ensure we have valid data cacheRemoteData(data, for: url) } @@ -145,7 +145,3 @@ final class PathConfigurationLoader { } } } - -private extension URL { - static let PathDataTemporaryURL = URL(string: "https://localhost/path-configuration.json")! -}