Sources/Confidence/DefaultStorage.swift (84 lines of code) (raw):

import Foundation class DefaultStorage: Storage { private let storageQueue = DispatchQueue(label: "com.confidence.storage") private let resolverCacheBundleId = "com.confidence.cache" private let filePath: String public init(filePath: String) { self.filePath = filePath } public func save(data: Encodable) throws { try storageQueue.sync { let encoded = try JSONEncoder().encode(data) let configUrl = try getConfigUrl() if !FileManager.default.fileExists(atPath: configUrl.backport.path) { try FileManager.default.createDirectory( at: configUrl.deletingLastPathComponent(), withIntermediateDirectories: true) } do { try encoded.write(to: configUrl, options: .atomic) } catch { throw ConfidenceError.cacheError(message: "Unable to encode: \(error)") } } } public func load<T>(defaultValue: T) throws -> T where T: Decodable { guard let data = try read() else { return defaultValue } do { return try JSONDecoder().decode(T.self, from: data) } catch { throw ConfidenceError.corruptedCache(message: "Unable to decode: \(error)") } } public func clear() throws { try storageQueue.sync { let configUrl = try getConfigUrl() if !FileManager.default.fileExists(atPath: configUrl.backport.path) { return } do { try FileManager.default.removeItem(atPath: configUrl.backport.path) } catch { throw ConfidenceError.cacheError(message: "Unable to clear cache: \(error)") } } } public func isEmpty() -> Bool { guard let data = try? read() else { return true } return data.isEmpty } func read() throws -> Data? { try storageQueue.sync { let configUrl = try getConfigUrl() guard FileManager.default.fileExists(atPath: configUrl.backport.path) else { return nil } let data = try { do { return try Data(contentsOf: configUrl) } catch { throw ConfidenceError.cacheError(message: "Unable to load cache file: \(error)") } }() return data } } public func getConfigUrl() throws -> URL { guard let applicationSupportUrl: URL = FileManager.default.urls( for: .applicationSupportDirectory, in: .userDomainMask) .last else { throw ConfidenceError.cacheError(message: "Could not get URL for application directory") } guard let bundleIdentifier = Bundle.main.bundleIdentifier else { throw ConfidenceError.cacheError(message: "Unable to get bundle identifier") } return applicationSupportUrl.backport.appending( components: resolverCacheBundleId, "\(bundleIdentifier)", filePath) } }