From beedc302f26c033350586f567578f717aea39696 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 09:48:26 +0200 Subject: [PATCH 01/31] Add generic Storage --- Cache.xcodeproj/project.pbxproj | 40 ++++ Source/Shared/Library/Entry2.swift | 17 ++ Source/Shared/Library/Transformer.swift | 11 + Source/Shared/Storage/DiskStorage2.swift | 233 +++++++++++++++++++++ Source/Shared/Storage/MemoryStorage.swift | 2 +- Source/Shared/Storage/MemoryStorage2.swift | 55 +++++ Source/Shared/Storage/StorageAware2.swift | 79 +++++++ 7 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 Source/Shared/Library/Entry2.swift create mode 100644 Source/Shared/Library/Transformer.swift create mode 100644 Source/Shared/Storage/DiskStorage2.swift create mode 100644 Source/Shared/Storage/MemoryStorage2.swift create mode 100644 Source/Shared/Storage/StorageAware2.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 4004aeb..d0d0ba2 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -41,7 +41,22 @@ D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984D1F694FFA00CE8F68 /* DiskConfig.swift */; }; D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */; }; D21B66A11F6A747000125DE1 /* ImageWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98761F69513800CE8F68 /* ImageWrapperTests.swift */; }; + D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; + D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; + D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; + D221E5C020D00DCC00BC940E /* Entry2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry2.swift */; }; + D221E5C120D00DCC00BC940E /* Entry2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry2.swift */; }; + D221E5C220D00DCC00BC940E /* Entry2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry2.swift */; }; + D221E5C420D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; + D221E5C520D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; + D221E5C620D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; D236F31A1F6BEF73004EE01F /* StorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236F3191F6BEF73004EE01F /* StorageTests.swift */; }; + D270147020D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; + D270147120D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; + D270147220D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; + D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; + D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; + D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -147,7 +162,12 @@ /* Begin PBXFileReference section */ BDEDD3561DBCE5B1007416A6 /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BDEDD3781DBCEB8A007416A6 /* Cache-tvOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cache-tvOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage2.swift; sourceTree = ""; }; + D221E5BF20D00DCC00BC940E /* Entry2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entry2.swift; sourceTree = ""; }; + D221E5C320D00DDB00BC940E /* DiskStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage2.swift; sourceTree = ""; }; D236F3191F6BEF73004EE01F /* StorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageTests.swift; sourceTree = ""; }; + D270146F20D1018A003B45C7 /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = ""; }; + D270147320D101F3003B45C7 /* StorageAware2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAware2.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -307,6 +327,8 @@ D2D4CC151FA3145000E4A2D5 /* MD5.swift */, D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */, D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */, + D221E5BF20D00DCC00BC940E /* Entry2.swift */, + D270146F20D1018A003B45C7 /* Transformer.swift */, ); path = Library; sourceTree = ""; @@ -323,6 +345,9 @@ D292DAF01F6A85F30060F614 /* SyncStorage.swift */, D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */, D292DAF81F6A962D0060F614 /* AsyncStorage.swift */, + D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */, + D221E5C320D00DDB00BC940E /* DiskStorage2.swift */, + D270147320D101F3003B45C7 /* StorageAware2.swift */, ); path = Storage; sourceTree = ""; @@ -768,16 +793,20 @@ buildActionMask = 2147483647; files = ( D21B66861F6A723C00125DE1 /* Entry.swift in Sources */, + D221E5C620D00DDB00BC940E /* DiskStorage2.swift in Sources */, D21B66971F6A724000125DE1 /* StorageAware.swift in Sources */, D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */, D21B66961F6A724000125DE1 /* Storage.swift in Sources */, D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */, D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */, + D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */, D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */, D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */, + D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */, D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */, D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */, + D270147220D1018A003B45C7 /* Transformer.swift in Sources */, D21B668C1F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, D292DAFF1F6A970B0060F614 /* Result.swift in Sources */, @@ -792,6 +821,7 @@ D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D21B66931F6A724000125DE1 /* DiskStorage.swift in Sources */, D21B66981F6A724000125DE1 /* TypeWrapperStorage.swift in Sources */, + D221E5C220D00DCC00BC940E /* Entry2.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -838,16 +868,20 @@ buildActionMask = 2147483647; files = ( D21B667D1F6A723C00125DE1 /* Entry.swift in Sources */, + D221E5C520D00DDB00BC940E /* DiskStorage2.swift in Sources */, D21B66911F6A723F00125DE1 /* StorageAware.swift in Sources */, D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */, D21B66901F6A723F00125DE1 /* Storage.swift in Sources */, D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */, D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */, + D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */, D21B669C1F6A724600125DE1 /* MemoryConfig.swift in Sources */, D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */, + D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */, D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */, D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */, + D270147120D1018A003B45C7 /* Transformer.swift in Sources */, D21B66831F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, D292DAFE1F6A970B0060F614 /* Result.swift in Sources */, @@ -862,6 +896,7 @@ D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D21B668D1F6A723F00125DE1 /* DiskStorage.swift in Sources */, D21B66921F6A723F00125DE1 /* TypeWrapperStorage.swift in Sources */, + D221E5C120D00DCC00BC940E /* Entry2.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -886,16 +921,20 @@ buildActionMask = 2147483647; files = ( D2CF98651F694FFA00CE8F68 /* Entry.swift in Sources */, + D221E5C420D00DDB00BC940E /* DiskStorage2.swift in Sources */, D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */, D2CF986C1F694FFA00CE8F68 /* HybridStorage.swift in Sources */, D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */, D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */, D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */, + D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */, D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */, D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */, + D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */, D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */, D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */, + D270147020D1018A003B45C7 /* Transformer.swift in Sources */, D292DAFD1F6A970B0060F614 /* Result.swift in Sources */, D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */, @@ -910,6 +949,7 @@ D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */, D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */, + D221E5C020D00DCC00BC940E /* Entry2.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/Shared/Library/Entry2.swift b/Source/Shared/Library/Entry2.swift new file mode 100644 index 0000000..4cc3c7c --- /dev/null +++ b/Source/Shared/Library/Entry2.swift @@ -0,0 +1,17 @@ +import Foundation + +/// A wrapper around cached object and its expiry date. +public struct Entry2 { + /// Cached object + public let object: T + /// Expiry date + public let expiry: Expiry + /// Associated meta data, if any + public let meta: [String: Any] + + init(object: T, expiry: Expiry, meta: [String: Any] = [:]) { + self.object = object + self.expiry = expiry + self.meta = meta + } +} diff --git a/Source/Shared/Library/Transformer.swift b/Source/Shared/Library/Transformer.swift new file mode 100644 index 0000000..7a22a41 --- /dev/null +++ b/Source/Shared/Library/Transformer.swift @@ -0,0 +1,11 @@ +import Foundation + +class Transformer { + let toData: (T) -> Data + let fromData: (Data) -> T + + init(toData: @escaping (T) -> Data, fromData: @escaping (Data) -> T) { + self.toData = toData + self.fromData = fromData + } +} diff --git a/Source/Shared/Storage/DiskStorage2.swift b/Source/Shared/Storage/DiskStorage2.swift new file mode 100644 index 0000000..8320588 --- /dev/null +++ b/Source/Shared/Storage/DiskStorage2.swift @@ -0,0 +1,233 @@ +import Foundation + +/// Save objects to file on disk +final class DiskStorage2: StorageAware2 { + enum Error: Swift.Error { + case fileEnumeratorFailed + } + + /// File manager to read/write to the disk + let fileManager: FileManager + /// Configuration + fileprivate let config: DiskConfig + /// The computed path `directory+name` + let path: String + + private let transformer: Transformer + + // MARK: - Initialization + + required init(config: DiskConfig, fileManager: FileManager = FileManager.default, transformer: Transformer) throws { + self.config = config + self.fileManager = fileManager + self.transformer = transformer + + let url: URL + if let directory = config.directory { + url = directory + } else { + url = try fileManager.url( + for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true + ) + } + + // path + self.path = url.appendingPathComponent(config.name, isDirectory: true).path + + try createDirectory() + + // protection + #if os(iOS) || os(tvOS) + if let protectionType = config.protectionType { + try setDirectoryAttributes([ + FileAttributeKey.protectionKey: protectionType + ]) + } + #endif + } + + func entry(forKey key: String) throws -> Entry2 { + let filePath = makeFilePath(for: key) + let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) + let attributes = try fileManager.attributesOfItem(atPath: filePath) + let object = transformer.fromData(data) + + guard let date = attributes[.modificationDate] as? Date else { + throw StorageError.malformedFileAttributes + } + + let meta: [String: Any] = [ + "filePath": filePath + ] + + return Entry2( + object: object, + expiry: Expiry.date(date), + meta: meta + ) + } + + func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { + let expiry = expiry ?? config.expiry + let data = transformer.toData(object) + let filePath = makeFilePath(for: key) + _ = fileManager.createFile(atPath: filePath, contents: data, attributes: nil) + try fileManager.setAttributes([.modificationDate: expiry.date], ofItemAtPath: filePath) + } + + func removeObject(forKey key: String) throws { + try fileManager.removeItem(atPath: makeFilePath(for: key)) + } + + func removeAll() throws { + try fileManager.removeItem(atPath: path) + try createDirectory() + } + + func removeExpiredObjects() throws { + let storageURL = URL(fileURLWithPath: path) + let resourceKeys: [URLResourceKey] = [ + .isDirectoryKey, + .contentModificationDateKey, + .totalFileAllocatedSizeKey + ] + var resourceObjects = [ResourceObject2]() + var filesToDelete = [URL]() + var totalSize: UInt = 0 + let fileEnumerator = fileManager.enumerator( + at: storageURL, + includingPropertiesForKeys: resourceKeys, + options: .skipsHiddenFiles, + errorHandler: nil + ) + + guard let urlArray = fileEnumerator?.allObjects as? [URL] else { + throw Error.fileEnumeratorFailed + } + + for url in urlArray { + let resourceValues = try url.resourceValues(forKeys: Set(resourceKeys)) + guard resourceValues.isDirectory != true else { + continue + } + + if let expiryDate = resourceValues.contentModificationDate, expiryDate.inThePast { + filesToDelete.append(url) + continue + } + + if let fileSize = resourceValues.totalFileAllocatedSize { + totalSize += UInt(fileSize) + resourceObjects.append((url: url, resourceValues: resourceValues)) + } + } + + // Remove expired objects + for url in filesToDelete { + try fileManager.removeItem(at: url) + } + + // Remove objects if storage size exceeds max size + try removeResourceObjects(resourceObjects, totalSize: totalSize) + } +} + +extension DiskStorage2 { + /** + Sets attributes on the disk cache folder. + - Parameter attributes: Directory attributes + */ + func setDirectoryAttributes(_ attributes: [FileAttributeKey: Any]) throws { + try fileManager.setAttributes(attributes, ofItemAtPath: path) + } +} + +typealias ResourceObject2 = (url: Foundation.URL, resourceValues: URLResourceValues) + +extension DiskStorage2 { + /** + Builds file name from the key. + - Parameter key: Unique key to identify the object in the cache + - Returns: A md5 string + */ + func makeFileName(for key: String) -> String { + return MD5(key) + } + + /** + Builds file path from the key. + - Parameter key: Unique key to identify the object in the cache + - Returns: A string path based on key + */ + func makeFilePath(for key: String) -> String { + return "\(path)/\(makeFileName(for: key))" + } + + /// Calculates total disk cache size. + func totalSize() throws -> UInt64 { + var size: UInt64 = 0 + let contents = try fileManager.contentsOfDirectory(atPath: path) + for pathComponent in contents { + let filePath = NSString(string: path).appendingPathComponent(pathComponent) + let attributes = try fileManager.attributesOfItem(atPath: filePath) + if let fileSize = attributes[.size] as? UInt64 { + size += fileSize + } + } + return size + } + + func createDirectory() throws { + guard !fileManager.fileExists(atPath: path) else { + return + } + + try fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, + attributes: nil) + } + + /** + Removes objects if storage size exceeds max size. + - Parameter objects: Resource objects to remove + - Parameter totalSize: Total size + */ + func removeResourceObjects(_ objects: [ResourceObject], totalSize: UInt) throws { + guard config.maxSize > 0 && totalSize > config.maxSize else { + return + } + + var totalSize = totalSize + let targetSize = config.maxSize / 2 + + let sortedFiles = objects.sorted { + if let time1 = $0.resourceValues.contentModificationDate?.timeIntervalSinceReferenceDate, + let time2 = $1.resourceValues.contentModificationDate?.timeIntervalSinceReferenceDate { + return time1 > time2 + } else { + return false + } + } + + for file in sortedFiles { + try fileManager.removeItem(at: file.url) + if let fileSize = file.resourceValues.totalFileAllocatedSize { + totalSize -= UInt(fileSize) + } + if totalSize < targetSize { + break + } + } + } + + /** + Removes the object from the cache if it's expired. + - Parameter key: Unique key to identify the object in the cache + */ + func removeObjectIfExpired(forKey key: String) throws { + let filePath = makeFilePath(for: key) + let attributes = try fileManager.attributesOfItem(atPath: filePath) + if let expiryDate = attributes[.modificationDate] as? Date, expiryDate.inThePast { + try fileManager.removeItem(atPath: filePath) + } + } +} diff --git a/Source/Shared/Storage/MemoryStorage.swift b/Source/Shared/Storage/MemoryStorage.swift index bb48138..43f5be1 100644 --- a/Source/Shared/Storage/MemoryStorage.swift +++ b/Source/Shared/Storage/MemoryStorage.swift @@ -1,4 +1,4 @@ -import Foundation +import UIKit /// Save objects to memory based on NSCache final class MemoryStorage { diff --git a/Source/Shared/Storage/MemoryStorage2.swift b/Source/Shared/Storage/MemoryStorage2.swift new file mode 100644 index 0000000..f7eaf6a --- /dev/null +++ b/Source/Shared/Storage/MemoryStorage2.swift @@ -0,0 +1,55 @@ +import Foundation + +class MemoryStorage2: StorageAware2 { + fileprivate let cache = NSCache() + // Memory cache keys + fileprivate var keys = Set() + /// Configuration + fileprivate let config: MemoryConfig + + init(config: MemoryConfig) { + self.config = config + self.cache.countLimit = Int(config.countLimit) + } + + func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) { + let capsule = MemoryCapsule(value: object, expiry: .date(expiry?.date ?? config.expiry.date)) + cache.setObject(capsule, forKey: NSString(string: key)) + keys.insert(key) + } + + func removeAll() { + cache.removeAllObjects() + keys.removeAll() + } + + func removeExpiredObjects() { + let allKeys = keys + for key in allKeys { + removeObjectIfExpired(forKey: key) + } + } + + func removeObjectIfExpired(forKey key: String) { + if let capsule = cache.object(forKey: NSString(string: key)), capsule.expiry.isExpired { + removeObject(forKey: key) + } + } + + func removeObject(forKey key: String) { + cache.removeObject(forKey: NSString(string: key)) + keys.remove(key) + } + + func entry(forKey key: String) throws -> Entry2 { + guard let capsule = cache.object(forKey: NSString(string: key)) else { + throw StorageError.notFound + } + + guard let object = capsule.object as? T else { + throw StorageError.typeNotMatch + } + + return Entry2(object: object, expiry: capsule.expiry) + } +} diff --git a/Source/Shared/Storage/StorageAware2.swift b/Source/Shared/Storage/StorageAware2.swift new file mode 100644 index 0000000..62bd13c --- /dev/null +++ b/Source/Shared/Storage/StorageAware2.swift @@ -0,0 +1,79 @@ +import Foundation + +/// A protocol used for saving and loading from storage +public protocol StorageAware2 { + associatedtype T + /** + Tries to retrieve the object from the storage. + - Parameter key: Unique key to identify the object in the cache + - Returns: Cached object or nil if not found + */ + func object(forKey key: String) throws -> T + + /** + Get cache entry which includes object with metadata. + - Parameter key: Unique key to identify the object in the cache + - Returns: Object wrapper with metadata or nil if not found + */ + func entry(forKey key: String) throws -> Entry2 + + /** + Removes the object by the given key. + - Parameter key: Unique key to identify the object. + */ + func removeObject(forKey key: String) throws + + /** + Saves passed object. + - Parameter key: Unique key to identify the object in the cache. + - Parameter object: Object that needs to be cached. + - Parameter expiry: Overwrite expiry for this object only. + */ + func setObject(_ object: T, forKey key: String, expiry: Expiry?) throws + + /** + Check if an object exist by the given key. + - Parameter key: Unique key to identify the object. + */ + func existsObject(forKey key: String) throws -> Bool + + /** + Removes all objects from the cache storage. + */ + func removeAll() throws + + /** + Clears all expired objects. + */ + func removeExpiredObjects() throws + + /** + Check if an expired object by the given key. + - Parameter key: Unique key to identify the object. + */ + func isExpiredObject(forKey key: String) throws -> Bool +} + +public extension StorageAware2 { + func object(forKey key: String) throws -> T { + return try entry(forKey: key).object + } + + func existsObject(forKey key: String) throws -> Bool { + do { + let _: T = try object(forKey: key) + return true + } catch { + return false + } + } + + func isExpiredObject(forKey key: String) throws -> Bool { + do { + let entry = try self.entry(forKey: key) + return entry.expiry.isExpired + } catch { + return true + } + } +} From 0b8f5c340ee0226fe7e7b2516bd21632929e2876 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 09:52:03 +0200 Subject: [PATCH 02/31] Add HybridStorage2 --- Cache.xcodeproj/project.pbxproj | 8 ++++ Source/Shared/Storage/HybridStorage2.swift | 45 ++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Source/Shared/Storage/HybridStorage2.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index d0d0ba2..89082fa 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -57,6 +57,9 @@ D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; + D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; + D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; + D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -168,6 +171,7 @@ D236F3191F6BEF73004EE01F /* StorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageTests.swift; sourceTree = ""; }; D270146F20D1018A003B45C7 /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = ""; }; D270147320D101F3003B45C7 /* StorageAware2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAware2.swift; sourceTree = ""; }; + D270147720D1046A003B45C7 /* HybridStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridStorage2.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -348,6 +352,7 @@ D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */, D221E5C320D00DDB00BC940E /* DiskStorage2.swift */, D270147320D101F3003B45C7 /* StorageAware2.swift */, + D270147720D1046A003B45C7 /* HybridStorage2.swift */, ); path = Storage; sourceTree = ""; @@ -816,6 +821,7 @@ D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */, D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, + D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */, D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */, D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */, @@ -891,6 +897,7 @@ D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */, D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */, D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, + D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */, D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */, D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */, @@ -944,6 +951,7 @@ D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */, D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */, D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, + D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */, D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */, D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */, diff --git a/Source/Shared/Storage/HybridStorage2.swift b/Source/Shared/Storage/HybridStorage2.swift new file mode 100644 index 0000000..14ba2bb --- /dev/null +++ b/Source/Shared/Storage/HybridStorage2.swift @@ -0,0 +1,45 @@ +import Foundation + +/// Use both memory and disk storage. Try on memory first. +class HybridStorage2 { + let memoryStorage: MemoryStorage2 + let diskStorage: DiskStorage2 + + init(memoryStorage: MemoryStorage2, diskStorage: DiskStorage2) { + self.memoryStorage = memoryStorage + self.diskStorage = diskStorage + } +} + +extension HybridStorage2: StorageAware2 { + func entry(forKey key: String) throws -> Entry2 { + do { + return try memoryStorage.entry(forKey: key) + } catch { + let entry = try diskStorage.entry(forKey: key) + // set back to memoryStorage + memoryStorage.setObject(entry.object, forKey: key, expiry: entry.expiry) + return entry + } + } + + func removeObject(forKey key: String) throws { + memoryStorage.removeObject(forKey: key) + try diskStorage.removeObject(forKey: key) + } + + func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { + memoryStorage.setObject(object, forKey: key, expiry: expiry) + try diskStorage.setObject(object, forKey: key, expiry: expiry) + } + + func removeAll() throws { + memoryStorage.removeAll() + try diskStorage.removeAll() + } + + func removeExpiredObjects() throws { + memoryStorage.removeExpiredObjects() + try diskStorage.removeExpiredObjects() + } +} From 7e9ffad42ac3d23cc9a38f743de4ab494437e713 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 10:05:10 +0200 Subject: [PATCH 03/31] Add function to support types --- Source/Shared/Storage/DiskStorage2.swift | 19 ++++++++++++++++--- Source/Shared/Storage/HybridStorage2.swift | 11 +++++++++++ Source/Shared/Storage/MemoryStorage2.swift | 7 +++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Source/Shared/Storage/DiskStorage2.swift b/Source/Shared/Storage/DiskStorage2.swift index 8320588..9eb1ab4 100644 --- a/Source/Shared/Storage/DiskStorage2.swift +++ b/Source/Shared/Storage/DiskStorage2.swift @@ -7,11 +7,11 @@ final class DiskStorage2: StorageAware2 { } /// File manager to read/write to the disk - let fileManager: FileManager + private let fileManager: FileManager /// Configuration fileprivate let config: DiskConfig /// The computed path `directory+name` - let path: String + private let path: String private let transformer: Transformer @@ -41,7 +41,7 @@ final class DiskStorage2: StorageAware2 { if let protectionType = config.protectionType { try setDirectoryAttributes([ FileAttributeKey.protectionKey: protectionType - ]) + ]) } #endif } @@ -231,3 +231,16 @@ extension DiskStorage2 { } } } + +extension DiskStorage2 { + func support(transformer: Transformer) -> DiskStorage2 { + // swiftlint:disable force_try + let storage = try! DiskStorage2( + config: config, + fileManager: fileManager, + transformer: transformer + ) + + return storage + } +} diff --git a/Source/Shared/Storage/HybridStorage2.swift b/Source/Shared/Storage/HybridStorage2.swift index 14ba2bb..e9c81a5 100644 --- a/Source/Shared/Storage/HybridStorage2.swift +++ b/Source/Shared/Storage/HybridStorage2.swift @@ -43,3 +43,14 @@ extension HybridStorage2: StorageAware2 { try diskStorage.removeExpiredObjects() } } + +extension HybridStorage2 { + func support(transformer: Transformer) -> HybridStorage2 { + let storage = HybridStorage2( + memoryStorage: memoryStorage.support(), + diskStorage: diskStorage.support(transformer: transformer) + ) + + return storage + } +} diff --git a/Source/Shared/Storage/MemoryStorage2.swift b/Source/Shared/Storage/MemoryStorage2.swift index f7eaf6a..cb4185d 100644 --- a/Source/Shared/Storage/MemoryStorage2.swift +++ b/Source/Shared/Storage/MemoryStorage2.swift @@ -53,3 +53,10 @@ class MemoryStorage2: StorageAware2 { return Entry2(object: object, expiry: capsule.expiry) } } + +extension MemoryStorage2 { + func support() -> MemoryStorage2 { + let storage = MemoryStorage2(config: config) + return storage + } +} From 187035bf720bdb518041a8d5f545a3efd0c3b5ee Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 10:15:05 +0200 Subject: [PATCH 04/31] Add support on SyncStorage2 --- Cache.xcodeproj/project.pbxproj | 16 ++++++ Source/Shared/Storage/Storage2.swift | 65 ++++++++++++++++++++++++ Source/Shared/Storage/SyncStorage2.swift | 60 ++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 Source/Shared/Storage/Storage2.swift create mode 100644 Source/Shared/Storage/SyncStorage2.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 89082fa..97d7339 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -60,6 +60,12 @@ D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; + D270147C20D107DA003B45C7 /* SyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage2.swift */; }; + D270147D20D107DA003B45C7 /* SyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage2.swift */; }; + D270147E20D107DA003B45C7 /* SyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage2.swift */; }; + D270148020D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; + D270148120D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; + D270148220D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -172,6 +178,8 @@ D270146F20D1018A003B45C7 /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = ""; }; D270147320D101F3003B45C7 /* StorageAware2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAware2.swift; sourceTree = ""; }; D270147720D1046A003B45C7 /* HybridStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridStorage2.swift; sourceTree = ""; }; + D270147B20D107DA003B45C7 /* SyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage2.swift; sourceTree = ""; }; + D270147F20D10982003B45C7 /* Storage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage2.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -353,6 +361,8 @@ D221E5C320D00DDB00BC940E /* DiskStorage2.swift */, D270147320D101F3003B45C7 /* StorageAware2.swift */, D270147720D1046A003B45C7 /* HybridStorage2.swift */, + D270147B20D107DA003B45C7 /* SyncStorage2.swift */, + D270147F20D10982003B45C7 /* Storage2.swift */, ); path = Storage; sourceTree = ""; @@ -814,6 +824,7 @@ D270147220D1018A003B45C7 /* Transformer.swift in Sources */, D21B668C1F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, + D270147E20D107DA003B45C7 /* SyncStorage2.swift in Sources */, D292DAFF1F6A970B0060F614 /* Result.swift in Sources */, D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */, D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */, @@ -825,6 +836,7 @@ D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */, D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */, + D270148220D10982003B45C7 /* Storage2.swift in Sources */, D21B66931F6A724000125DE1 /* DiskStorage.swift in Sources */, D21B66981F6A724000125DE1 /* TypeWrapperStorage.swift in Sources */, D221E5C220D00DCC00BC940E /* Entry2.swift in Sources */, @@ -890,6 +902,7 @@ D270147120D1018A003B45C7 /* Transformer.swift in Sources */, D21B66831F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, + D270147D20D107DA003B45C7 /* SyncStorage2.swift in Sources */, D292DAFE1F6A970B0060F614 /* Result.swift in Sources */, D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */, D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */, @@ -901,6 +914,7 @@ D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */, D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */, + D270148120D10982003B45C7 /* Storage2.swift in Sources */, D21B668D1F6A723F00125DE1 /* DiskStorage.swift in Sources */, D21B66921F6A723F00125DE1 /* TypeWrapperStorage.swift in Sources */, D221E5C120D00DCC00BC940E /* Entry2.swift in Sources */, @@ -944,6 +958,7 @@ D270147020D1018A003B45C7 /* Transformer.swift in Sources */, D292DAFD1F6A970B0060F614 /* Result.swift in Sources */, D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, + D270147C20D107DA003B45C7 /* SyncStorage2.swift in Sources */, D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */, D2CF986F1F694FFA00CE8F68 /* StorageAware.swift in Sources */, D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */, @@ -955,6 +970,7 @@ D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */, D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */, + D270148020D10982003B45C7 /* Storage2.swift in Sources */, D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */, D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */, D221E5C020D00DCC00BC940E /* Entry2.swift in Sources */, diff --git a/Source/Shared/Storage/Storage2.swift b/Source/Shared/Storage/Storage2.swift new file mode 100644 index 0000000..fe45c45 --- /dev/null +++ b/Source/Shared/Storage/Storage2.swift @@ -0,0 +1,65 @@ +import Foundation +import Dispatch + +/// Manage storage. Use memory storage if specified. +/// Synchronous by default. Use `async` for asynchronous operations. +public class Storage2 { + /// Used for sync operations + fileprivate let syncStorage: SyncStorage + + /// Storage used internally by both sync and async storages + private let interalStorage: StorageAware + + /// Initialize storage with configuration options. + /// + /// - Parameters: + /// - diskConfig: Configuration for disk storage + /// - memoryConfig: Optional. Pass confi if you want memory cache + /// - Throws: Throw StorageError if any. + public required init(diskConfig: DiskConfig, memoryConfig: MemoryConfig? = nil) throws { + // Disk or Hybrid + let storage: StorageAware + let disk = try DiskStorage(config: diskConfig) + + if let memoryConfig = memoryConfig { + let memory = MemoryStorage(config: memoryConfig) + storage = HybridStorage(memoryStorage: memory, diskStorage: disk) + } else { + storage = disk + } + + // Wrapper + self.interalStorage = TypeWrapperStorage(storage: storage) + + // Sync + self.sync = SyncStorage(storage: interalStorage, + serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")) + } + + /// Used for async operations + public lazy var async: AsyncStorage = AsyncStorage(storage: self.interalStorage, + serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")) +} + +extension Storage: StorageAware { + public func entry(ofType type: T.Type, forKey key: String) throws -> Entry { + return try self.sync.entry(ofType: type, forKey: key) + } + + public func removeObject(forKey key: String) throws { + try self.sync.removeObject(forKey: key) + } + + public func setObject(_ object: T, forKey key: String, + expiry: Expiry? = nil) throws { + try self.sync.setObject(object, forKey: key, expiry: expiry) + } + + public func removeAll() throws { + try self.sync.removeAll() + } + + public func removeExpiredObjects() throws { + try self.sync.removeExpiredObjects() + } +} diff --git a/Source/Shared/Storage/SyncStorage2.swift b/Source/Shared/Storage/SyncStorage2.swift new file mode 100644 index 0000000..af2c08e --- /dev/null +++ b/Source/Shared/Storage/SyncStorage2.swift @@ -0,0 +1,60 @@ +import Foundation +import Dispatch + +/// Manipulate storage in a "all sync" manner. +/// Block the current queue until the operation completes. +public class SyncStorage2 { + fileprivate let innerStorage: HybridStorage2 + fileprivate let serialQueue: DispatchQueue + + init(innerStorage: HybridStorage2, serialQueue: DispatchQueue) { + self.innerStorage = innerStorage + self.serialQueue = serialQueue + } +} + +extension SyncStorage2: StorageAware2 { + public func entry(forKey key: String) throws -> Entry2 { + var entry: Entry2! + try serialQueue.sync { + entry = try innerStorage.entry(forKey: key) + } + + return entry + } + + public func removeObject(forKey key: String) throws { + try serialQueue.sync { + try self.innerStorage.removeObject(forKey: key) + } + } + + public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { + try serialQueue.sync { + try innerStorage.setObject(object, forKey: key, expiry: expiry) + } + } + + public func removeAll() throws { + try serialQueue.sync { + try innerStorage.removeAll() + } + } + + public func removeExpiredObjects() throws { + try serialQueue.sync { + try innerStorage.removeExpiredObjects() + } + } +} + +extension SyncStorage2 { + func support(transformer: Transformer) -> SyncStorage2 { + let storage = SyncStorage2( + innerStorage: innerStorage.support(transformer: transformer), + serialQueue: serialQueue + ) + + return storage + } +} From aa664e19348e847d5e9d24c9af422b1749fee571 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 10:30:42 +0200 Subject: [PATCH 05/31] Add Storage2 --- Source/Shared/Library/Transformer.swift | 4 +- Source/Shared/Storage/Storage2.swift | 55 ++++++++++--------------- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/Source/Shared/Library/Transformer.swift b/Source/Shared/Library/Transformer.swift index 7a22a41..ef3f35f 100644 --- a/Source/Shared/Library/Transformer.swift +++ b/Source/Shared/Library/Transformer.swift @@ -1,10 +1,10 @@ import Foundation -class Transformer { +public class Transformer { let toData: (T) -> Data let fromData: (Data) -> T - init(toData: @escaping (T) -> Data, fromData: @escaping (Data) -> T) { + public init(toData: @escaping (T) -> Data, fromData: @escaping (Data) -> T) { self.toData = toData self.fromData = fromData } diff --git a/Source/Shared/Storage/Storage2.swift b/Source/Shared/Storage/Storage2.swift index fe45c45..2b39e86 100644 --- a/Source/Shared/Storage/Storage2.swift +++ b/Source/Shared/Storage/Storage2.swift @@ -5,61 +5,50 @@ import Dispatch /// Synchronous by default. Use `async` for asynchronous operations. public class Storage2 { /// Used for sync operations - fileprivate let syncStorage: SyncStorage - - /// Storage used internally by both sync and async storages - private let interalStorage: StorageAware + fileprivate let syncStorage: SyncStorage2 /// Initialize storage with configuration options. /// /// - Parameters: /// - diskConfig: Configuration for disk storage - /// - memoryConfig: Optional. Pass confi if you want memory cache + /// - memoryConfig: Optional. Pass config if you want memory cache /// - Throws: Throw StorageError if any. - public required init(diskConfig: DiskConfig, memoryConfig: MemoryConfig? = nil) throws { - // Disk or Hybrid - let storage: StorageAware - let disk = try DiskStorage(config: diskConfig) + public required init(diskConfig: DiskConfig, memoryConfig: MemoryConfig, transformer: Transformer) throws { + let disk = try DiskStorage2(config: diskConfig, transformer: transformer) + let memory = MemoryStorage2(config: memoryConfig) - if let memoryConfig = memoryConfig { - let memory = MemoryStorage(config: memoryConfig) - storage = HybridStorage(memoryStorage: memory, diskStorage: disk) - } else { - storage = disk - } - - // Wrapper - self.interalStorage = TypeWrapperStorage(storage: storage) - - // Sync - self.sync = SyncStorage(storage: interalStorage, - serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")) + let hybridStorage = HybridStorage2(memoryStorage: memory, diskStorage: disk) + self.syncStorage = SyncStorage2( + innerStorage: hybridStorage, + serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue") + ) } /// Used for async operations - public lazy var async: AsyncStorage = AsyncStorage(storage: self.interalStorage, - serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")) +// public lazy var async: AsyncStorage = AsyncStorage( +// storage: self.interalStorage, +// serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue") +// ) } -extension Storage: StorageAware { - public func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - return try self.sync.entry(ofType: type, forKey: key) +extension Storage2: StorageAware2 { + public func entry(forKey key: String) throws -> Entry2 { + return try self.syncStorage.entry(forKey: key) } public func removeObject(forKey key: String) throws { - try self.sync.removeObject(forKey: key) + try self.syncStorage.removeObject(forKey: key) } - public func setObject(_ object: T, forKey key: String, - expiry: Expiry? = nil) throws { - try self.sync.setObject(object, forKey: key, expiry: expiry) + public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { + try self.syncStorage.setObject(object, forKey: key, expiry: expiry) } public func removeAll() throws { - try self.sync.removeAll() + try self.syncStorage.removeAll() } public func removeExpiredObjects() throws { - try self.sync.removeExpiredObjects() + try self.syncStorage.removeExpiredObjects() } } From 4c48dfb08453f1be109551cc229456148f92669c Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 10:36:20 +0200 Subject: [PATCH 06/31] Add AsyncStorage2 --- Cache.xcodeproj/project.pbxproj | 8 ++ Source/Shared/Storage/AsyncStorage2.swift | 118 ++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 Source/Shared/Storage/AsyncStorage2.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 97d7339..c862fbe 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -66,6 +66,9 @@ D270148020D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; D270148120D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; D270148220D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; + D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; + D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; + D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -180,6 +183,7 @@ D270147720D1046A003B45C7 /* HybridStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridStorage2.swift; sourceTree = ""; }; D270147B20D107DA003B45C7 /* SyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage2.swift; sourceTree = ""; }; D270147F20D10982003B45C7 /* Storage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage2.swift; sourceTree = ""; }; + D270148320D10E76003B45C7 /* AsyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage2.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -363,6 +367,7 @@ D270147720D1046A003B45C7 /* HybridStorage2.swift */, D270147B20D107DA003B45C7 /* SyncStorage2.swift */, D270147F20D10982003B45C7 /* Storage2.swift */, + D270148320D10E76003B45C7 /* AsyncStorage2.swift */, ); path = Storage; sourceTree = ""; @@ -834,6 +839,7 @@ D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */, D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */, + D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */, D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D270148220D10982003B45C7 /* Storage2.swift in Sources */, @@ -912,6 +918,7 @@ D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */, D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */, + D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */, D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D270148120D10982003B45C7 /* Storage2.swift in Sources */, @@ -968,6 +975,7 @@ D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */, D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */, + D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */, D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D270148020D10982003B45C7 /* Storage2.swift in Sources */, diff --git a/Source/Shared/Storage/AsyncStorage2.swift b/Source/Shared/Storage/AsyncStorage2.swift new file mode 100644 index 0000000..1319958 --- /dev/null +++ b/Source/Shared/Storage/AsyncStorage2.swift @@ -0,0 +1,118 @@ +import Foundation +import Dispatch + +/// Manipulate storage in a "all async" manner. +/// The completion closure will be called when operation completes. +public class AsyncStorage2 { + fileprivate let innerStorage: HybridStorage2 + public let serialQueue: DispatchQueue + + init(storage: HybridStorage2, serialQueue: DispatchQueue) { + self.innerStorage = storage + self.serialQueue = serialQueue + } +} + +extension AsyncStorage2 { + public func entry(forKey key: String, completion: @escaping (Result>) -> Void) { + serialQueue.async { [weak self] in + guard let `self` = self else { + completion(Result.error(StorageError.deallocated)) + return + } + + do { + let anEntry = try self.innerStorage.entry(forKey: key) + completion(Result.value(anEntry)) + } catch { + completion(Result.error(error)) + } + } + } + + public func removeObject(forKey key: String, completion: @escaping (Result<()>) -> Void) { + serialQueue.async { [weak self] in + guard let `self` = self else { + completion(Result.error(StorageError.deallocated)) + return + } + + do { + try self.innerStorage.removeObject(forKey: key) + completion(Result.value(())) + } catch { + completion(Result.error(error)) + } + } + } + + public func setObject( + _ object: T, + forKey key: String, + expiry: Expiry? = nil, + completion: @escaping (Result<()>) -> Void) { + serialQueue.async { [weak self] in + guard let `self` = self else { + completion(Result.error(StorageError.deallocated)) + return + } + + do { + try self.innerStorage.setObject(object, forKey: key, expiry: expiry) + completion(Result.value(())) + } catch { + completion(Result.error(error)) + } + } + } + + public func removeAll(completion: @escaping (Result<()>) -> Void) { + serialQueue.async { [weak self] in + guard let `self` = self else { + completion(Result.error(StorageError.deallocated)) + return + } + + do { + try self.innerStorage.removeAll() + completion(Result.value(())) + } catch { + completion(Result.error(error)) + } + } + } + + public func removeExpiredObjects(completion: @escaping (Result<()>) -> Void) { + serialQueue.async { [weak self] in + guard let `self` = self else { + completion(Result.error(StorageError.deallocated)) + return + } + + do { + try self.innerStorage.removeExpiredObjects() + completion(Result.value(())) + } catch { + completion(Result.error(error)) + } + } + } + + func object(forKey key: String, completion: @escaping (Result) -> Void) { + entry(forKey: key, completion: { (result: Result>) in + completion(result.map({ entry in + return entry.object + })) + }) + } + + func existsObject( + forKey key: String, + completion: @escaping (Result) -> Void) { + object(forKey: key, completion: { (result: Result) in + completion(result.map({ _ in + return true + })) + }) + } +} From 760d267b81f41b939ac2745014dc86431ac52187 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 10:41:00 +0200 Subject: [PATCH 07/31] Add AsyncStorage2 --- Source/Shared/Storage/Storage2.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Shared/Storage/Storage2.swift b/Source/Shared/Storage/Storage2.swift index 2b39e86..de53ee4 100644 --- a/Source/Shared/Storage/Storage2.swift +++ b/Source/Shared/Storage/Storage2.swift @@ -6,6 +6,7 @@ import Dispatch public class Storage2 { /// Used for sync operations fileprivate let syncStorage: SyncStorage2 + fileprivate let asyncStorage: AsyncStorage2 /// Initialize storage with configuration options. /// @@ -22,13 +23,15 @@ public class Storage2 { innerStorage: hybridStorage, serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue") ) + + self.asyncStorage = AsyncStorage2( + storage: hybridStorage, + serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue") + ) } /// Used for async operations -// public lazy var async: AsyncStorage = AsyncStorage( -// storage: self.interalStorage, -// serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue") -// ) + public lazy var async = self.asyncStorage } extension Storage2: StorageAware2 { From d3e9a71a398b5f0f9648f87798a73d1b844217b8 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 10:51:45 +0200 Subject: [PATCH 08/31] Expose all public --- Cache.xcodeproj/project.pbxproj | 8 ++++++ Source/Shared/Storage/AsyncStorage2.swift | 15 +++++++++-- Source/Shared/Storage/DiskStorage2.swift | 2 ++ Source/Shared/Storage/HybridStorage2.swift | 14 +++++----- Source/Shared/Storage/MemoryStorage2.swift | 20 +++++++------- Source/Shared/Storage/Storage2.swift | 31 ++++++++++++++++++---- Source/Shared/Storage/StorageFactory.swift | 19 +++++++++++++ Source/Shared/Storage/SyncStorage2.swift | 2 +- 8 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 Source/Shared/Storage/StorageFactory.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index c862fbe..3fdbbc7 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -69,6 +69,9 @@ D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; + D270148820D11040003B45C7 /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* StorageFactory.swift */; }; + D270148920D11040003B45C7 /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* StorageFactory.swift */; }; + D270148A20D11040003B45C7 /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* StorageFactory.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -184,6 +187,7 @@ D270147B20D107DA003B45C7 /* SyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage2.swift; sourceTree = ""; }; D270147F20D10982003B45C7 /* Storage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage2.swift; sourceTree = ""; }; D270148320D10E76003B45C7 /* AsyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage2.swift; sourceTree = ""; }; + D270148720D11040003B45C7 /* StorageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageFactory.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -368,6 +372,7 @@ D270147B20D107DA003B45C7 /* SyncStorage2.swift */, D270147F20D10982003B45C7 /* Storage2.swift */, D270148320D10E76003B45C7 /* AsyncStorage2.swift */, + D270148720D11040003B45C7 /* StorageFactory.swift */, ); path = Storage; sourceTree = ""; @@ -835,6 +840,7 @@ D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */, D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */, + D270148A20D11040003B45C7 /* StorageFactory.swift in Sources */, D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */, @@ -914,6 +920,7 @@ D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */, D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */, + D270148920D11040003B45C7 /* StorageFactory.swift in Sources */, D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */, D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */, @@ -971,6 +978,7 @@ D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */, D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */, D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */, + D270148820D11040003B45C7 /* StorageFactory.swift in Sources */, D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */, D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */, diff --git a/Source/Shared/Storage/AsyncStorage2.swift b/Source/Shared/Storage/AsyncStorage2.swift index 1319958..0e7ea0f 100644 --- a/Source/Shared/Storage/AsyncStorage2.swift +++ b/Source/Shared/Storage/AsyncStorage2.swift @@ -98,7 +98,7 @@ extension AsyncStorage2 { } } - func object(forKey key: String, completion: @escaping (Result) -> Void) { + public func object(forKey key: String, completion: @escaping (Result) -> Void) { entry(forKey: key, completion: { (result: Result>) in completion(result.map({ entry in return entry.object @@ -106,7 +106,7 @@ extension AsyncStorage2 { }) } - func existsObject( + public func existsObject( forKey key: String, completion: @escaping (Result) -> Void) { object(forKey: key, completion: { (result: Result) in @@ -116,3 +116,14 @@ extension AsyncStorage2 { }) } } + +public extension AsyncStorage2 { + func support(transformer: Transformer) -> AsyncStorage2 { + let storage = AsyncStorage2( + storage: innerStorage.support(transformer: transformer), + serialQueue: serialQueue + ) + + return storage + } +} diff --git a/Source/Shared/Storage/DiskStorage2.swift b/Source/Shared/Storage/DiskStorage2.swift index 9eb1ab4..078926f 100644 --- a/Source/Shared/Storage/DiskStorage2.swift +++ b/Source/Shared/Storage/DiskStorage2.swift @@ -45,7 +45,9 @@ final class DiskStorage2: StorageAware2 { } #endif } +} +extension DiskStorage2 { func entry(forKey key: String) throws -> Entry2 { let filePath = makeFilePath(for: key) let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) diff --git a/Source/Shared/Storage/HybridStorage2.swift b/Source/Shared/Storage/HybridStorage2.swift index e9c81a5..05d27ad 100644 --- a/Source/Shared/Storage/HybridStorage2.swift +++ b/Source/Shared/Storage/HybridStorage2.swift @@ -1,7 +1,7 @@ import Foundation /// Use both memory and disk storage. Try on memory first. -class HybridStorage2 { +public class HybridStorage2 { let memoryStorage: MemoryStorage2 let diskStorage: DiskStorage2 @@ -12,7 +12,7 @@ class HybridStorage2 { } extension HybridStorage2: StorageAware2 { - func entry(forKey key: String) throws -> Entry2 { + public func entry(forKey key: String) throws -> Entry2 { do { return try memoryStorage.entry(forKey: key) } catch { @@ -23,28 +23,28 @@ extension HybridStorage2: StorageAware2 { } } - func removeObject(forKey key: String) throws { + public func removeObject(forKey key: String) throws { memoryStorage.removeObject(forKey: key) try diskStorage.removeObject(forKey: key) } - func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { + public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { memoryStorage.setObject(object, forKey: key, expiry: expiry) try diskStorage.setObject(object, forKey: key, expiry: expiry) } - func removeAll() throws { + public func removeAll() throws { memoryStorage.removeAll() try diskStorage.removeAll() } - func removeExpiredObjects() throws { + public func removeExpiredObjects() throws { memoryStorage.removeExpiredObjects() try diskStorage.removeExpiredObjects() } } -extension HybridStorage2 { +public extension HybridStorage2 { func support(transformer: Transformer) -> HybridStorage2 { let storage = HybridStorage2( memoryStorage: memoryStorage.support(), diff --git a/Source/Shared/Storage/MemoryStorage2.swift b/Source/Shared/Storage/MemoryStorage2.swift index cb4185d..df2baf8 100644 --- a/Source/Shared/Storage/MemoryStorage2.swift +++ b/Source/Shared/Storage/MemoryStorage2.swift @@ -1,47 +1,49 @@ import Foundation -class MemoryStorage2: StorageAware2 { +public class MemoryStorage2: StorageAware2 { fileprivate let cache = NSCache() // Memory cache keys fileprivate var keys = Set() /// Configuration fileprivate let config: MemoryConfig - init(config: MemoryConfig) { + public init(config: MemoryConfig) { self.config = config self.cache.countLimit = Int(config.countLimit) } +} - func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) { +extension MemoryStorage2 { + public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) { let capsule = MemoryCapsule(value: object, expiry: .date(expiry?.date ?? config.expiry.date)) cache.setObject(capsule, forKey: NSString(string: key)) keys.insert(key) } - func removeAll() { + public func removeAll() { cache.removeAllObjects() keys.removeAll() } - func removeExpiredObjects() { + public func removeExpiredObjects() { let allKeys = keys for key in allKeys { removeObjectIfExpired(forKey: key) } } - func removeObjectIfExpired(forKey key: String) { + public func removeObjectIfExpired(forKey key: String) { if let capsule = cache.object(forKey: NSString(string: key)), capsule.expiry.isExpired { removeObject(forKey: key) } } - func removeObject(forKey key: String) { + public func removeObject(forKey key: String) { cache.removeObject(forKey: NSString(string: key)) keys.remove(key) } - func entry(forKey key: String) throws -> Entry2 { + public func entry(forKey key: String) throws -> Entry2 { guard let capsule = cache.object(forKey: NSString(string: key)) else { throw StorageError.notFound } @@ -54,7 +56,7 @@ class MemoryStorage2: StorageAware2 { } } -extension MemoryStorage2 { +public extension MemoryStorage2 { func support() -> MemoryStorage2 { let storage = MemoryStorage2(config: config) return storage diff --git a/Source/Shared/Storage/Storage2.swift b/Source/Shared/Storage/Storage2.swift index de53ee4..60f7b9c 100644 --- a/Source/Shared/Storage/Storage2.swift +++ b/Source/Shared/Storage/Storage2.swift @@ -5,8 +5,8 @@ import Dispatch /// Synchronous by default. Use `async` for asynchronous operations. public class Storage2 { /// Used for sync operations - fileprivate let syncStorage: SyncStorage2 - fileprivate let asyncStorage: AsyncStorage2 + let syncStorage: SyncStorage2 + let asyncStorage: AsyncStorage2 /// Initialize storage with configuration options. /// @@ -14,20 +14,31 @@ public class Storage2 { /// - diskConfig: Configuration for disk storage /// - memoryConfig: Optional. Pass config if you want memory cache /// - Throws: Throw StorageError if any. - public required init(diskConfig: DiskConfig, memoryConfig: MemoryConfig, transformer: Transformer) throws { + public convenience init(diskConfig: DiskConfig, memoryConfig: MemoryConfig, transformer: Transformer) throws { let disk = try DiskStorage2(config: diskConfig, transformer: transformer) let memory = MemoryStorage2(config: memoryConfig) let hybridStorage = HybridStorage2(memoryStorage: memory, diskStorage: disk) - self.syncStorage = SyncStorage2( + let syncStorage = SyncStorage2( innerStorage: hybridStorage, serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue") ) - self.asyncStorage = AsyncStorage2( + let asyncStorage = AsyncStorage2( storage: hybridStorage, serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue") ) + + self.init(syncStorage: syncStorage, asyncStorage: asyncStorage) + } + + /// Initialise with sync and async storages + /// + /// - Parameter syncStorage: Synchronous storage + /// - Paraeter: asyncStorage: Asynchronous storage + public required init(syncStorage: SyncStorage2, asyncStorage: AsyncStorage2) { + self.syncStorage = syncStorage + self.asyncStorage = asyncStorage } /// Used for async operations @@ -55,3 +66,13 @@ extension Storage2: StorageAware2 { try self.syncStorage.removeExpiredObjects() } } + +public extension Storage2 { + func support(transformer: Transformer) -> Storage2 { + let storage = Storage2( + syncStorage: syncStorage.support(transformer: transformer), + asyncStorage: asyncStorage.support(transformer: transformer) + ) + return storage + } +} diff --git a/Source/Shared/Storage/StorageFactory.swift b/Source/Shared/Storage/StorageFactory.swift new file mode 100644 index 0000000..111d487 --- /dev/null +++ b/Source/Shared/Storage/StorageFactory.swift @@ -0,0 +1,19 @@ +// +// StorageFactory.swift +// Cache +// +// Created by Khoa Pham on 13.06.2018. +// Copyright © 2018 Hyper Interaktiv AS. All rights reserved. +// + +import Foundation + +public extension Storage2 { + func supportImage() -> Storage2 { + fatalError() + } + + func supportCodable() -> Storage2 { + fatalError() + } +} diff --git a/Source/Shared/Storage/SyncStorage2.swift b/Source/Shared/Storage/SyncStorage2.swift index af2c08e..f43119a 100644 --- a/Source/Shared/Storage/SyncStorage2.swift +++ b/Source/Shared/Storage/SyncStorage2.swift @@ -48,7 +48,7 @@ extension SyncStorage2: StorageAware2 { } } -extension SyncStorage2 { +public extension SyncStorage2 { func support(transformer: Transformer) -> SyncStorage2 { let storage = SyncStorage2( innerStorage: innerStorage.support(transformer: transformer), From 56c4483a1163037e0a2deec7cd589ba1cc341db2 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 11:02:54 +0200 Subject: [PATCH 09/31] Add factory methods to Transformer --- Cache.xcodeproj/project.pbxproj | 24 ++++++++++++------- .../Shared/Library/Optional+Extension.swift | 11 +++++++++ Source/Shared/Library/StorageError.swift | 2 ++ Source/Shared/Library/Transformer.swift | 20 +++++++++++++--- Source/Shared/Storage/DiskStorage2.swift | 4 ++-- Source/Shared/Storage/Storage+Support.swift | 12 ++++++++++ Source/Shared/Storage/StorageFactory.swift | 19 --------------- 7 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 Source/Shared/Library/Optional+Extension.swift create mode 100644 Source/Shared/Storage/Storage+Support.swift delete mode 100644 Source/Shared/Storage/StorageFactory.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 3fdbbc7..2c5491d 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -69,9 +69,12 @@ D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; - D270148820D11040003B45C7 /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* StorageFactory.swift */; }; - D270148920D11040003B45C7 /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* StorageFactory.swift */; }; - D270148A20D11040003B45C7 /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* StorageFactory.swift */; }; + D270148820D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; + D270148920D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; + D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; + D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; + D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; + D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -187,7 +190,8 @@ D270147B20D107DA003B45C7 /* SyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage2.swift; sourceTree = ""; }; D270147F20D10982003B45C7 /* Storage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage2.swift; sourceTree = ""; }; D270148320D10E76003B45C7 /* AsyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage2.swift; sourceTree = ""; }; - D270148720D11040003B45C7 /* StorageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageFactory.swift; sourceTree = ""; }; + D270148720D11040003B45C7 /* Storage+Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Support.swift"; sourceTree = ""; }; + D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -349,6 +353,7 @@ D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */, D221E5BF20D00DCC00BC940E /* Entry2.swift */, D270146F20D1018A003B45C7 /* Transformer.swift */, + D270148B20D113EA003B45C7 /* Optional+Extension.swift */, ); path = Library; sourceTree = ""; @@ -372,7 +377,7 @@ D270147B20D107DA003B45C7 /* SyncStorage2.swift */, D270147F20D10982003B45C7 /* Storage2.swift */, D270148320D10E76003B45C7 /* AsyncStorage2.swift */, - D270148720D11040003B45C7 /* StorageFactory.swift */, + D270148720D11040003B45C7 /* Storage+Support.swift */, ); path = Storage; sourceTree = ""; @@ -830,6 +835,7 @@ D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */, D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */, + D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */, D270147220D1018A003B45C7 /* Transformer.swift in Sources */, D21B668C1F6A723C00125DE1 /* Types.swift in Sources */, @@ -840,7 +846,7 @@ D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */, D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */, - D270148A20D11040003B45C7 /* StorageFactory.swift in Sources */, + D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */, D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */, @@ -910,6 +916,7 @@ D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */, D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */, + D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */, D270147120D1018A003B45C7 /* Transformer.swift in Sources */, D21B66831F6A723C00125DE1 /* Types.swift in Sources */, @@ -920,7 +927,7 @@ D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */, D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */, - D270148920D11040003B45C7 /* StorageFactory.swift in Sources */, + D270148920D11040003B45C7 /* Storage+Support.swift in Sources */, D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */, D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */, @@ -968,6 +975,7 @@ D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */, D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */, + D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */, D270147020D1018A003B45C7 /* Transformer.swift in Sources */, D292DAFD1F6A970B0060F614 /* Result.swift in Sources */, @@ -978,7 +986,7 @@ D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */, D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */, D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */, - D270148820D11040003B45C7 /* StorageFactory.swift in Sources */, + D270148820D11040003B45C7 /* Storage+Support.swift in Sources */, D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */, D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */, diff --git a/Source/Shared/Library/Optional+Extension.swift b/Source/Shared/Library/Optional+Extension.swift new file mode 100644 index 0000000..69516b2 --- /dev/null +++ b/Source/Shared/Library/Optional+Extension.swift @@ -0,0 +1,11 @@ +import Foundation + +public extension Optional { + func unwrapOrThrow(error: Error) throws -> Wrapped { + if let value = self { + return value + } else { + throw error + } + } +} diff --git a/Source/Shared/Library/StorageError.swift b/Source/Shared/Library/StorageError.swift index 96dcf20..18bd7c1 100644 --- a/Source/Shared/Library/StorageError.swift +++ b/Source/Shared/Library/StorageError.swift @@ -13,4 +13,6 @@ public enum StorageError: Error { case encodingFailed /// The storage has been deallocated case deallocated + /// Fail to perform transformation to or from Data + case transformerFail } diff --git a/Source/Shared/Library/Transformer.swift b/Source/Shared/Library/Transformer.swift index ef3f35f..4c20288 100644 --- a/Source/Shared/Library/Transformer.swift +++ b/Source/Shared/Library/Transformer.swift @@ -1,11 +1,25 @@ import Foundation public class Transformer { - let toData: (T) -> Data - let fromData: (Data) -> T + let toData: (T) throws -> Data + let fromData: (Data) throws -> T - public init(toData: @escaping (T) -> Data, fromData: @escaping (Data) -> T) { + public init(toData: @escaping (T) throws -> Data, fromData: @escaping (Data) throws -> T) { self.toData = toData self.fromData = fromData } } + +extension Transformer { + static func imageTransformer() -> Transformer { + let toData: (Image) throws -> Data = { image in + return try image.cache_toData().unwrapOrThrow(error: StorageError.transformerFail) + } + + let fromData: (Data) throws -> Image = { data in + return try Image(data: data).unwrapOrThrow(error: StorageError.transformerFail) + } + + return Transformer(toData: toData, fromData: fromData) + } +} diff --git a/Source/Shared/Storage/DiskStorage2.swift b/Source/Shared/Storage/DiskStorage2.swift index 078926f..52193c0 100644 --- a/Source/Shared/Storage/DiskStorage2.swift +++ b/Source/Shared/Storage/DiskStorage2.swift @@ -52,7 +52,7 @@ extension DiskStorage2 { let filePath = makeFilePath(for: key) let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) let attributes = try fileManager.attributesOfItem(atPath: filePath) - let object = transformer.fromData(data) + let object = try transformer.fromData(data) guard let date = attributes[.modificationDate] as? Date else { throw StorageError.malformedFileAttributes @@ -71,7 +71,7 @@ extension DiskStorage2 { func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { let expiry = expiry ?? config.expiry - let data = transformer.toData(object) + let data = try transformer.toData(object) let filePath = makeFilePath(for: key) _ = fileManager.createFile(atPath: filePath, contents: data, attributes: nil) try fileManager.setAttributes([.modificationDate: expiry.date], ofItemAtPath: filePath) diff --git a/Source/Shared/Storage/Storage+Support.swift b/Source/Shared/Storage/Storage+Support.swift new file mode 100644 index 0000000..24d1268 --- /dev/null +++ b/Source/Shared/Storage/Storage+Support.swift @@ -0,0 +1,12 @@ +import Foundation + +public extension Storage2 { + func supportImage() -> Storage2 { + let storage = support(transformer: Transformer.imageTransformer()) + return storage + } + + func supportCodable() -> Storage2 { + fatalError() + } +} diff --git a/Source/Shared/Storage/StorageFactory.swift b/Source/Shared/Storage/StorageFactory.swift deleted file mode 100644 index 111d487..0000000 --- a/Source/Shared/Storage/StorageFactory.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// StorageFactory.swift -// Cache -// -// Created by Khoa Pham on 13.06.2018. -// Copyright © 2018 Hyper Interaktiv AS. All rights reserved. -// - -import Foundation - -public extension Storage2 { - func supportImage() -> Storage2 { - fatalError() - } - - func supportCodable() -> Storage2 { - fatalError() - } -} From 12abe4f48c99dfeb919b47f436ce9cc0ccb831aa Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 11:09:00 +0200 Subject: [PATCH 10/31] Add Transformer for Codable and for Data --- Source/Shared/Library/Transformer.swift | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Source/Shared/Library/Transformer.swift b/Source/Shared/Library/Transformer.swift index 4c20288..6210ee0 100644 --- a/Source/Shared/Library/Transformer.swift +++ b/Source/Shared/Library/Transformer.swift @@ -11,7 +11,15 @@ public class Transformer { } extension Transformer { - static func imageTransformer() -> Transformer { + static func forData() -> Transformer { + let toData: (Data) throws -> Data = { $0 } + + let fromData: (Data) throws -> Data = { $0 } + + return Transformer(toData: toData, fromData: fromData) + } + + static func forImage() -> Transformer { let toData: (Image) throws -> Data = { image in return try image.cache_toData().unwrapOrThrow(error: StorageError.transformerFail) } @@ -22,4 +30,18 @@ extension Transformer { return Transformer(toData: toData, fromData: fromData) } + + static func forCodable(ofType: U) -> Transformer { + let toData: (U) throws -> Data = { object in + let encoder = JSONEncoder() + return try encoder.encode(object) + } + + let fromData: (Data) throws -> U = { data in + let decoder = JSONDecoder() + return try decoder.decode(U.self, from: data) + } + + return Transformer(toData: toData, fromData: fromData) + } } From 74f9794d228d07aa709ccd2481e7978cf98da021 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:10:55 +0200 Subject: [PATCH 11/31] Fix type annotation --- Source/Shared/Storage/Storage+Support.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/Shared/Storage/Storage+Support.swift b/Source/Shared/Storage/Storage+Support.swift index 24d1268..74014e2 100644 --- a/Source/Shared/Storage/Storage+Support.swift +++ b/Source/Shared/Storage/Storage+Support.swift @@ -1,12 +1,18 @@ import Foundation public extension Storage2 { - func supportImage() -> Storage2 { - let storage = support(transformer: Transformer.imageTransformer()) + func supportData() -> Storage2 { + let storage = support(transformer: Transformer.forData()) return storage } - func supportCodable() -> Storage2 { - fatalError() + func supportImage() -> Storage2 { + let storage = support(transformer: Transformer.forImage()) + return storage + } + + func supportCodable(ofType: U.Type) -> Storage2 { + let storage = support(transformer: Transformer.forCodable(ofType: U.self)) + return storage } } From 9cbd6bdbdcc8fc142c38474f3f0762a1e332a9dc Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:12:08 +0200 Subject: [PATCH 12/31] Use TypeWrapper to work with primitive types --- Cache.xcodeproj/project.pbxproj | 8 ++++++++ Source/Shared/Library/Transformer.swift | 7 ++++--- Source/Shared/Library/TypeWrapper.swift | 14 ++++++++++++++ Source/Shared/Storage/TypeWrapperStorage.swift | 13 ------------- 4 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 Source/Shared/Library/TypeWrapper.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 2c5491d..f799d40 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -75,6 +75,9 @@ D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; + D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; }; + D270149120D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; }; + D270149220D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -192,6 +195,7 @@ D270148320D10E76003B45C7 /* AsyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage2.swift; sourceTree = ""; }; D270148720D11040003B45C7 /* Storage+Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Support.swift"; sourceTree = ""; }; D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; + D270148F20D1251E003B45C7 /* TypeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapper.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; @@ -354,6 +358,7 @@ D221E5BF20D00DCC00BC940E /* Entry2.swift */, D270146F20D1018A003B45C7 /* Transformer.swift */, D270148B20D113EA003B45C7 /* Optional+Extension.swift */, + D270148F20D1251E003B45C7 /* TypeWrapper.swift */, ); path = Library; sourceTree = ""; @@ -831,6 +836,7 @@ D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */, D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */, D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */, + D270149220D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */, @@ -912,6 +918,7 @@ D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */, D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */, D21B669C1F6A724600125DE1 /* MemoryConfig.swift in Sources */, + D270149120D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */, @@ -971,6 +978,7 @@ D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */, D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */, D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */, + D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */, diff --git a/Source/Shared/Library/Transformer.swift b/Source/Shared/Library/Transformer.swift index 6210ee0..e2ccbca 100644 --- a/Source/Shared/Library/Transformer.swift +++ b/Source/Shared/Library/Transformer.swift @@ -31,15 +31,16 @@ extension Transformer { return Transformer(toData: toData, fromData: fromData) } - static func forCodable(ofType: U) -> Transformer { + static func forCodable(ofType: U.Type) -> Transformer { let toData: (U) throws -> Data = { object in + let wrapper = TypeWrapper(object: object) let encoder = JSONEncoder() - return try encoder.encode(object) + return try encoder.encode(wrapper) } let fromData: (Data) throws -> U = { data in let decoder = JSONDecoder() - return try decoder.decode(U.self, from: data) + return try decoder.decode(TypeWrapper.self, from: data).object } return Transformer(toData: toData, fromData: fromData) diff --git a/Source/Shared/Library/TypeWrapper.swift b/Source/Shared/Library/TypeWrapper.swift new file mode 100644 index 0000000..f30ea05 --- /dev/null +++ b/Source/Shared/Library/TypeWrapper.swift @@ -0,0 +1,14 @@ +import Foundation + +/// Used to wrap Codable object +public struct TypeWrapper: Codable { + enum CodingKeys: String, CodingKey { + case object + } + + public let object: T + + public init(object: T) { + self.object = object + } +} diff --git a/Source/Shared/Storage/TypeWrapperStorage.swift b/Source/Shared/Storage/TypeWrapperStorage.swift index 26c0717..cabbd4a 100644 --- a/Source/Shared/Storage/TypeWrapperStorage.swift +++ b/Source/Shared/Storage/TypeWrapperStorage.swift @@ -35,16 +35,3 @@ extension TypeWrapperStorage: StorageAware { try internalStorage.removeExpiredObjects() } } - -/// Used to wrap Codable object -struct TypeWrapper: Codable { - enum CodingKeys: String, CodingKey { - case object - } - - let object: T - - init(object: T) { - self.object = object - } -} From 4346ba490ddb4116d96e693bfea3812d04eaf84d Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:13:50 +0200 Subject: [PATCH 13/31] Remove old Storage files --- Cache.xcodeproj/project.pbxproj | 104 ++------ Source/Shared/Library/Entry.swift | 2 +- Source/Shared/Library/Entry2.swift | 17 -- Source/Shared/Library/MemoryCapsule.swift | 20 ++ Source/Shared/Storage/AsyncStorage.swift | 99 -------- Source/Shared/Storage/AsyncStorage2.swift | 4 +- Source/Shared/Storage/AsyncStorageAware.swift | 82 ------- Source/Shared/Storage/DiskStorage.swift | 231 ------------------ Source/Shared/Storage/DiskStorage2.swift | 8 +- Source/Shared/Storage/HybridStorage.swift | 45 ---- Source/Shared/Storage/HybridStorage2.swift | 2 +- Source/Shared/Storage/MemoryStorage.swift | 86 ------- Source/Shared/Storage/MemoryStorage2.swift | 4 +- Source/Shared/Storage/Storage.swift | 65 ----- Source/Shared/Storage/Storage2.swift | 2 +- Source/Shared/Storage/StorageAware.swift | 78 ------ Source/Shared/Storage/StorageAware2.swift | 2 +- Source/Shared/Storage/SyncStorage.swift | 50 ---- Source/Shared/Storage/SyncStorage2.swift | 4 +- .../Shared/Storage/TypeWrapperStorage.swift | 37 --- 20 files changed, 50 insertions(+), 892 deletions(-) delete mode 100644 Source/Shared/Library/Entry2.swift create mode 100644 Source/Shared/Library/MemoryCapsule.swift delete mode 100644 Source/Shared/Storage/AsyncStorage.swift delete mode 100644 Source/Shared/Storage/AsyncStorageAware.swift delete mode 100644 Source/Shared/Storage/DiskStorage.swift delete mode 100644 Source/Shared/Storage/HybridStorage.swift delete mode 100644 Source/Shared/Storage/MemoryStorage.swift delete mode 100644 Source/Shared/Storage/Storage.swift delete mode 100644 Source/Shared/Storage/StorageAware.swift delete mode 100644 Source/Shared/Storage/SyncStorage.swift delete mode 100644 Source/Shared/Storage/TypeWrapperStorage.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index f799d40..e8373bc 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -9,31 +9,17 @@ /* Begin PBXBuildFile section */ BDEDD37D1DBCEB8A007416A6 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDEDD3561DBCE5B1007416A6 /* Cache.framework */; }; D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */; }; - D21B667D1F6A723C00125DE1 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98541F694FFA00CE8F68 /* Entry.swift */; }; D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */; }; D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98561F694FFA00CE8F68 /* Expiry.swift */; }; D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */; }; D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98591F694FFA00CE8F68 /* StorageError.swift */; }; D21B66831F6A723C00125DE1 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; }; D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */; }; - D21B66861F6A723C00125DE1 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98541F694FFA00CE8F68 /* Entry.swift */; }; D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */; }; D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98561F694FFA00CE8F68 /* Expiry.swift */; }; D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */; }; D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98591F694FFA00CE8F68 /* StorageError.swift */; }; D21B668C1F6A723C00125DE1 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; }; - D21B668D1F6A723F00125DE1 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */; }; - D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */; }; - D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */; }; - D21B66901F6A723F00125DE1 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985E1F694FFA00CE8F68 /* Storage.swift */; }; - D21B66911F6A723F00125DE1 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */; }; - D21B66921F6A723F00125DE1 /* TypeWrapperStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */; }; - D21B66931F6A724000125DE1 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */; }; - D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */; }; - D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */; }; - D21B66961F6A724000125DE1 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985E1F694FFA00CE8F68 /* Storage.swift */; }; - D21B66971F6A724000125DE1 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */; }; - D21B66981F6A724000125DE1 /* TypeWrapperStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */; }; D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */; }; D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */; }; D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984D1F694FFA00CE8F68 /* DiskConfig.swift */; }; @@ -44,9 +30,9 @@ D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; - D221E5C020D00DCC00BC940E /* Entry2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry2.swift */; }; - D221E5C120D00DCC00BC940E /* Entry2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry2.swift */; }; - D221E5C220D00DCC00BC940E /* Entry2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry2.swift */; }; + D221E5C020D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; }; + D221E5C120D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; }; + D221E5C220D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; }; D221E5C420D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; D221E5C520D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; D221E5C620D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; @@ -78,6 +64,9 @@ D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; }; D270149120D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; }; D270149220D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; }; + D270149420D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; + D270149520D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; + D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -86,15 +75,6 @@ D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */; }; D28C9BAC1F67ECD400C180C1 /* TestHelper+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291CDF1C28374800B702C9 /* TestHelper+iOS.swift */; }; D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291DA01C28405900B702C9 /* UIImage+ExtensionsTests.swift */; }; - D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF01F6A85F30060F614 /* SyncStorage.swift */; }; - D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF01F6A85F30060F614 /* SyncStorage.swift */; }; - D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF01F6A85F30060F614 /* SyncStorage.swift */; }; - D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */; }; - D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */; }; - D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */; }; - D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF81F6A962D0060F614 /* AsyncStorage.swift */; }; - D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF81F6A962D0060F614 /* AsyncStorage.swift */; }; - D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF81F6A962D0060F614 /* AsyncStorage.swift */; }; D292DAFD1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; }; D292DAFE1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; }; D292DAFF1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; }; @@ -115,16 +95,10 @@ D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */; }; D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */; }; D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */; }; - D2CF98651F694FFA00CE8F68 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98541F694FFA00CE8F68 /* Entry.swift */; }; D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */; }; D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98561F694FFA00CE8F68 /* Expiry.swift */; }; D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */; }; D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98591F694FFA00CE8F68 /* StorageError.swift */; }; - D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */; }; - D2CF986C1F694FFA00CE8F68 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */; }; - D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */; }; - D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985E1F694FFA00CE8F68 /* Storage.swift */; }; - D2CF986F1F694FFA00CE8F68 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */; }; D2CF987C1F69513800CE8F68 /* Date+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98721F69513800CE8F68 /* Date+ExtensionsTests.swift */; }; D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98741F69513800CE8F68 /* MemoryCapsuleTests.swift */; }; D2CF987E1F69513800CE8F68 /* ExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98751F69513800CE8F68 /* ExpiryTests.swift */; }; @@ -133,7 +107,6 @@ D2CF98811F69513800CE8F68 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; }; D2CF98821F69513800CE8F68 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; D2CF98831F69513800CE8F68 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; }; - D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */; }; D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; }; D2CF98891F695F9400CE8F68 /* TypeWrapperStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */; }; D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; }; @@ -184,7 +157,7 @@ BDEDD3561DBCE5B1007416A6 /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BDEDD3781DBCEB8A007416A6 /* Cache-tvOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cache-tvOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage2.swift; sourceTree = ""; }; - D221E5BF20D00DCC00BC940E /* Entry2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entry2.swift; sourceTree = ""; }; + D221E5BF20D00DCC00BC940E /* Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entry.swift; sourceTree = ""; }; D221E5C320D00DDB00BC940E /* DiskStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage2.swift; sourceTree = ""; }; D236F3191F6BEF73004EE01F /* StorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageTests.swift; sourceTree = ""; }; D270146F20D1018A003B45C7 /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = ""; }; @@ -196,11 +169,9 @@ D270148720D11040003B45C7 /* Storage+Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Support.swift"; sourceTree = ""; }; D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; D270148F20D1251E003B45C7 /* TypeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapper.swift; sourceTree = ""; }; + D270149320D125AC003B45C7 /* MemoryCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCapsule.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; - D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; - D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorageAware.swift; sourceTree = ""; }; - D292DAF81F6A962D0060F614 /* AsyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage.swift; sourceTree = ""; }; D292DAFC1F6A970B0060F614 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorageTests.swift; sourceTree = ""; }; D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorageTests.swift; sourceTree = ""; }; @@ -211,16 +182,10 @@ D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryConfig.swift; sourceTree = ""; }; D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSerializer.swift; sourceTree = ""; }; - D2CF98541F694FFA00CE8F68 /* Entry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Entry.swift; sourceTree = ""; }; D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpirationMode.swift; sourceTree = ""; }; D2CF98561F694FFA00CE8F68 /* Expiry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Expiry.swift; sourceTree = ""; }; D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageWrapper.swift; sourceTree = ""; }; D2CF98591F694FFA00CE8F68 /* StorageError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageError.swift; sourceTree = ""; }; - D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskStorage.swift; sourceTree = ""; }; - D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HybridStorage.swift; sourceTree = ""; }; - D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = ""; }; - D2CF985E1F694FFA00CE8F68 /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; - D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageAware.swift; sourceTree = ""; }; D2CF98721F69513800CE8F68 /* Date+ExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+ExtensionsTests.swift"; sourceTree = ""; }; D2CF98741F69513800CE8F68 /* MemoryCapsuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryCapsuleTests.swift; sourceTree = ""; }; D2CF98751F69513800CE8F68 /* ExpiryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpiryTests.swift; sourceTree = ""; }; @@ -229,7 +194,6 @@ D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskStorageTests.swift; sourceTree = ""; }; D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HybridStorageTests.swift; sourceTree = ""; }; D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryStorageTests.swift; sourceTree = ""; }; - D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapperStorage.swift; sourceTree = ""; }; D2CF98861F695B8F00CE8F68 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapperStorageTests.swift; sourceTree = ""; }; D2D4CC151FA3145000E4A2D5 /* MD5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = ""; }; @@ -345,7 +309,6 @@ isa = PBXGroup; children = ( D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */, - D2CF98541F694FFA00CE8F68 /* Entry.swift */, D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */, D2CF98561F694FFA00CE8F68 /* Expiry.swift */, D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */, @@ -355,10 +318,11 @@ D2D4CC151FA3145000E4A2D5 /* MD5.swift */, D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */, D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */, - D221E5BF20D00DCC00BC940E /* Entry2.swift */, + D221E5BF20D00DCC00BC940E /* Entry.swift */, D270146F20D1018A003B45C7 /* Transformer.swift */, D270148B20D113EA003B45C7 /* Optional+Extension.swift */, D270148F20D1251E003B45C7 /* TypeWrapper.swift */, + D270149320D125AC003B45C7 /* MemoryCapsule.swift */, ); path = Library; sourceTree = ""; @@ -366,15 +330,6 @@ D2CF985A1F694FFA00CE8F68 /* Storage */ = { isa = PBXGroup; children = ( - D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */, - D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */, - D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */, - D2CF985E1F694FFA00CE8F68 /* Storage.swift */, - D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */, - D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */, - D292DAF01F6A85F30060F614 /* SyncStorage.swift */, - D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */, - D292DAF81F6A962D0060F614 /* AsyncStorage.swift */, D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */, D221E5C320D00DDB00BC940E /* DiskStorage2.swift */, D270147320D101F3003B45C7 /* StorageAware2.swift */, @@ -827,11 +782,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D21B66861F6A723C00125DE1 /* Entry.swift in Sources */, D221E5C620D00DDB00BC940E /* DiskStorage2.swift in Sources */, - D21B66971F6A724000125DE1 /* StorageAware.swift in Sources */, D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */, - D21B66961F6A724000125DE1 /* Storage.swift in Sources */, D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */, D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */, D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */, @@ -840,9 +792,7 @@ D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */, - D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */, D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */, - D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */, D270147220D1018A003B45C7 /* Transformer.swift in Sources */, D21B668C1F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, @@ -851,19 +801,15 @@ D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */, D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */, - D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */, D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */, D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */, - D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */, D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */, + D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */, D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, - D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D270148220D10982003B45C7 /* Storage2.swift in Sources */, - D21B66931F6A724000125DE1 /* DiskStorage.swift in Sources */, - D21B66981F6A724000125DE1 /* TypeWrapperStorage.swift in Sources */, - D221E5C220D00DCC00BC940E /* Entry2.swift in Sources */, + D221E5C220D00DCC00BC940E /* Entry.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -909,11 +855,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D21B667D1F6A723C00125DE1 /* Entry.swift in Sources */, D221E5C520D00DDB00BC940E /* DiskStorage2.swift in Sources */, - D21B66911F6A723F00125DE1 /* StorageAware.swift in Sources */, D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */, - D21B66901F6A723F00125DE1 /* Storage.swift in Sources */, D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */, D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */, D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */, @@ -922,9 +865,7 @@ D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */, - D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */, D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */, - D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */, D270147120D1018A003B45C7 /* Transformer.swift in Sources */, D21B66831F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, @@ -933,19 +874,15 @@ D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */, D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */, - D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */, D270148920D11040003B45C7 /* Storage+Support.swift in Sources */, D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */, D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */, - D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */, D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */, + D270149520D125AC003B45C7 /* MemoryCapsule.swift in Sources */, D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, - D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D270148120D10982003B45C7 /* Storage2.swift in Sources */, - D21B668D1F6A723F00125DE1 /* DiskStorage.swift in Sources */, - D21B66921F6A723F00125DE1 /* TypeWrapperStorage.swift in Sources */, - D221E5C120D00DCC00BC940E /* Entry2.swift in Sources */, + D221E5C120D00DCC00BC940E /* Entry.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -969,43 +906,34 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D2CF98651F694FFA00CE8F68 /* Entry.swift in Sources */, D221E5C420D00DDB00BC940E /* DiskStorage2.swift in Sources */, D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */, - D2CF986C1F694FFA00CE8F68 /* HybridStorage.swift in Sources */, D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */, D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */, D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */, D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */, - D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */, D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */, D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */, - D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */, D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */, D270147020D1018A003B45C7 /* Transformer.swift in Sources */, D292DAFD1F6A970B0060F614 /* Result.swift in Sources */, D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, D270147C20D107DA003B45C7 /* SyncStorage2.swift in Sources */, - D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */, - D2CF986F1F694FFA00CE8F68 /* StorageAware.swift in Sources */, - D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */, D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */, - D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */, D270148820D11040003B45C7 /* Storage+Support.swift in Sources */, D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */, D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */, - D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */, D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */, D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, - D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */, D270148020D10982003B45C7 /* Storage2.swift in Sources */, + D270149420D125AC003B45C7 /* MemoryCapsule.swift in Sources */, D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */, D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */, - D221E5C020D00DCC00BC940E /* Entry2.swift in Sources */, + D221E5C020D00DCC00BC940E /* Entry.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/Shared/Library/Entry.swift b/Source/Shared/Library/Entry.swift index b4317b7..6ccb7c0 100644 --- a/Source/Shared/Library/Entry.swift +++ b/Source/Shared/Library/Entry.swift @@ -1,7 +1,7 @@ import Foundation /// A wrapper around cached object and its expiry date. -public struct Entry { +public struct Entry { /// Cached object public let object: T /// Expiry date diff --git a/Source/Shared/Library/Entry2.swift b/Source/Shared/Library/Entry2.swift deleted file mode 100644 index 4cc3c7c..0000000 --- a/Source/Shared/Library/Entry2.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -/// A wrapper around cached object and its expiry date. -public struct Entry2 { - /// Cached object - public let object: T - /// Expiry date - public let expiry: Expiry - /// Associated meta data, if any - public let meta: [String: Any] - - init(object: T, expiry: Expiry, meta: [String: Any] = [:]) { - self.object = object - self.expiry = expiry - self.meta = meta - } -} diff --git a/Source/Shared/Library/MemoryCapsule.swift b/Source/Shared/Library/MemoryCapsule.swift new file mode 100644 index 0000000..180c850 --- /dev/null +++ b/Source/Shared/Library/MemoryCapsule.swift @@ -0,0 +1,20 @@ +import Foundation + +/// Helper class to hold cached instance and expiry date. +/// Used in memory storage to work with NSCache. +class MemoryCapsule: NSObject { + /// Object to be cached + let object: Any + /// Expiration date + let expiry: Expiry + + /** + Creates a new instance of Capsule. + - Parameter value: Object to be cached + - Parameter expiry: Expiration date + */ + init(value: Any, expiry: Expiry) { + self.object = value + self.expiry = expiry + } +} diff --git a/Source/Shared/Storage/AsyncStorage.swift b/Source/Shared/Storage/AsyncStorage.swift deleted file mode 100644 index 224ace2..0000000 --- a/Source/Shared/Storage/AsyncStorage.swift +++ /dev/null @@ -1,99 +0,0 @@ -import Foundation -import Dispatch - -/// Manipulate storage in a "all async" manner. -/// The completion closure will be called when operation completes. -public class AsyncStorage { - fileprivate let internalStorage: StorageAware - public let serialQueue: DispatchQueue - - init(storage: StorageAware, serialQueue: DispatchQueue) { - self.internalStorage = storage - self.serialQueue = serialQueue - } -} - -extension AsyncStorage: AsyncStorageAware { - public func entry(ofType type: T.Type, forKey key: String, completion: @escaping (Result>) -> Void) { - serialQueue.async { [weak self] in - guard let `self` = self else { - completion(Result.error(StorageError.deallocated)) - return - } - - do { - let anEntry = try self.internalStorage.entry(ofType: type, forKey: key) - completion(Result.value(anEntry)) - } catch { - completion(Result.error(error)) - } - } - } - - public func removeObject(forKey key: String, completion: @escaping (Result<()>) -> Void) { - serialQueue.async { [weak self] in - guard let `self` = self else { - completion(Result.error(StorageError.deallocated)) - return - } - - do { - try self.internalStorage.removeObject(forKey: key) - completion(Result.value(())) - } catch { - completion(Result.error(error)) - } - } - } - - public func setObject(_ object: T, - forKey key: String, - expiry: Expiry? = nil, - completion: @escaping (Result<()>) -> Void) { - serialQueue.async { [weak self] in - guard let `self` = self else { - completion(Result.error(StorageError.deallocated)) - return - } - - do { - try self.internalStorage.setObject(object, forKey: key, expiry: expiry) - completion(Result.value(())) - } catch { - completion(Result.error(error)) - } - } - } - - public func removeAll(completion: @escaping (Result<()>) -> Void) { - serialQueue.async { [weak self] in - guard let `self` = self else { - completion(Result.error(StorageError.deallocated)) - return - } - - do { - try self.internalStorage.removeAll() - completion(Result.value(())) - } catch { - completion(Result.error(error)) - } - } - } - - public func removeExpiredObjects(completion: @escaping (Result<()>) -> Void) { - serialQueue.async { [weak self] in - guard let `self` = self else { - completion(Result.error(StorageError.deallocated)) - return - } - - do { - try self.internalStorage.removeExpiredObjects() - completion(Result.value(())) - } catch { - completion(Result.error(error)) - } - } - } -} diff --git a/Source/Shared/Storage/AsyncStorage2.swift b/Source/Shared/Storage/AsyncStorage2.swift index 0e7ea0f..bbda340 100644 --- a/Source/Shared/Storage/AsyncStorage2.swift +++ b/Source/Shared/Storage/AsyncStorage2.swift @@ -14,7 +14,7 @@ public class AsyncStorage2 { } extension AsyncStorage2 { - public func entry(forKey key: String, completion: @escaping (Result>) -> Void) { + public func entry(forKey key: String, completion: @escaping (Result>) -> Void) { serialQueue.async { [weak self] in guard let `self` = self else { completion(Result.error(StorageError.deallocated)) @@ -99,7 +99,7 @@ extension AsyncStorage2 { } public func object(forKey key: String, completion: @escaping (Result) -> Void) { - entry(forKey: key, completion: { (result: Result>) in + entry(forKey: key, completion: { (result: Result>) in completion(result.map({ entry in return entry.object })) diff --git a/Source/Shared/Storage/AsyncStorageAware.swift b/Source/Shared/Storage/AsyncStorageAware.swift deleted file mode 100644 index a20995d..0000000 --- a/Source/Shared/Storage/AsyncStorageAware.swift +++ /dev/null @@ -1,82 +0,0 @@ -import Foundation -import Dispatch - -/// A protocol used for saving and loading from storage in async manner. -public protocol AsyncStorageAware: class { - /// All async operation must act on a serial queue. - var serialQueue: DispatchQueue { get } - - /** - Tries to retrieve the object from the storage. - - Parameter key: Unique key to identify the object in the cache. - - Parameter completion: Triggered until the operation completes. - */ - func object(ofType type: T.Type, forKey key: String, completion: @escaping (Result) -> Void) - - /** - Get cache entry which includes object with metadata. - - Parameter key: Unique key to identify the object in the cache - - Parameter completion: Triggered until the operation completes. - */ - func entry(ofType type: T.Type, forKey key: String, completion: @escaping (Result>) -> Void) - - /** - Removes the object by the given key. - - Parameter key: Unique key to identify the object. - - Parameter completion: Triggered until the operation completes. - */ - func removeObject(forKey key: String, completion: @escaping (Result<()>) -> Void) - - /** - Saves passed object. - - Parameter key: Unique key to identify the object in the cache. - - Parameter object: Object that needs to be cached. - - Parameter expiry: Overwrite expiry for this object only. - - Parameter completion: Triggered until the operation completes. - */ - func setObject(_ object: T, - forKey key: String, - expiry: Expiry?, - completion: @escaping (Result<()>) -> Void) - - /** - Check if an object exist by the given key. - - Parameter key: Unique key to identify the object. - - Parameter completion: Triggered until the operation completes. - */ - func existsObject(ofType type: T.Type, - forKey key: String, - completion: @escaping (Result) -> Void) - - /** - Removes all objects from the cache storage. - - Parameter completion: Triggered until the operation completes. - */ - func removeAll(completion: @escaping (Result<()>) -> Void) - - /** - Clears all expired objects. - - Parameter completion: Triggered until the operation completes. - */ - func removeExpiredObjects(completion: @escaping (Result<()>) -> Void) -} - -public extension AsyncStorageAware { - func object(ofType type: T.Type, forKey key: String, completion: @escaping (Result) -> Void) { - entry(ofType: type, forKey: key, completion: { (result: Result>) in - completion(result.map({ entry in - return entry.object - })) - }) - } - - func existsObject(ofType type: T.Type, - forKey key: String, - completion: @escaping (Result) -> Void) { - object(ofType: type, forKey: key, completion: { (result: Result) in - completion(result.map({ _ in - return true - })) - }) - } -} diff --git a/Source/Shared/Storage/DiskStorage.swift b/Source/Shared/Storage/DiskStorage.swift deleted file mode 100644 index 447da43..0000000 --- a/Source/Shared/Storage/DiskStorage.swift +++ /dev/null @@ -1,231 +0,0 @@ -import Foundation - -/// Save objects to file on disk -final class DiskStorage { - enum Error: Swift.Error { - case fileEnumeratorFailed - } - - /// File manager to read/write to the disk - fileprivate let fileManager: FileManager - /// Configuration - fileprivate let config: DiskConfig - /// The computed path `directory+name` - let path: String - - // MARK: - Initialization - - required init(config: DiskConfig, fileManager: FileManager = FileManager.default) throws { - self.config = config - self.fileManager = fileManager - - let url: URL - if let directory = config.directory { - url = directory - } else { - url = try fileManager.url( - for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true - ) - } - - // path - path = url.appendingPathComponent(config.name, isDirectory: true).path - try createDirectory() - - // protection - #if os(iOS) || os(tvOS) - if let protectionType = config.protectionType { - try setDirectoryAttributes([ - FileAttributeKey.protectionKey: protectionType - ]) - } - #endif - } -} - -extension DiskStorage: StorageAware { - func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - let filePath = makeFilePath(for: key) - let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) - let attributes = try fileManager.attributesOfItem(atPath: filePath) - let object: T = try DataSerializer.deserialize(data: data) - - guard let date = attributes[.modificationDate] as? Date else { - throw StorageError.malformedFileAttributes - } - - let meta: [String: Any] = [ - "filePath": filePath - ] - - return Entry( - object: object, - expiry: Expiry.date(date), - meta: meta - ) - } - - func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { - let expiry = expiry ?? config.expiry - let data = try DataSerializer.serialize(object: object) - let filePath = makeFilePath(for: key) - _ = fileManager.createFile(atPath: filePath, contents: data, attributes: nil) - try fileManager.setAttributes([.modificationDate: expiry.date], ofItemAtPath: filePath) - } - - func removeObject(forKey key: String) throws { - try fileManager.removeItem(atPath: makeFilePath(for: key)) - } - - func removeAll() throws { - try fileManager.removeItem(atPath: path) - try createDirectory() - } - - func removeExpiredObjects() throws { - let storageURL = URL(fileURLWithPath: path) - let resourceKeys: [URLResourceKey] = [ - .isDirectoryKey, - .contentModificationDateKey, - .totalFileAllocatedSizeKey - ] - var resourceObjects = [ResourceObject]() - var filesToDelete = [URL]() - var totalSize: UInt = 0 - let fileEnumerator = fileManager.enumerator( - at: storageURL, - includingPropertiesForKeys: resourceKeys, - options: .skipsHiddenFiles, - errorHandler: nil - ) - - guard let urlArray = fileEnumerator?.allObjects as? [URL] else { - throw Error.fileEnumeratorFailed - } - - for url in urlArray { - let resourceValues = try url.resourceValues(forKeys: Set(resourceKeys)) - guard resourceValues.isDirectory != true else { - continue - } - - if let expiryDate = resourceValues.contentModificationDate, expiryDate.inThePast { - filesToDelete.append(url) - continue - } - - if let fileSize = resourceValues.totalFileAllocatedSize { - totalSize += UInt(fileSize) - resourceObjects.append((url: url, resourceValues: resourceValues)) - } - } - - // Remove expired objects - for url in filesToDelete { - try fileManager.removeItem(at: url) - } - - // Remove objects if storage size exceeds max size - try removeResourceObjects(resourceObjects, totalSize: totalSize) - } -} - -extension DiskStorage { - /** - Sets attributes on the disk cache folder. - - Parameter attributes: Directory attributes - */ - func setDirectoryAttributes(_ attributes: [FileAttributeKey: Any]) throws { - try fileManager.setAttributes(attributes, ofItemAtPath: path) - } -} - -typealias ResourceObject = (url: Foundation.URL, resourceValues: URLResourceValues) - -extension DiskStorage { - /** - Builds file name from the key. - - Parameter key: Unique key to identify the object in the cache - - Returns: A md5 string - */ - func makeFileName(for key: String) -> String { - return MD5(key) - } - - /** - Builds file path from the key. - - Parameter key: Unique key to identify the object in the cache - - Returns: A string path based on key - */ - func makeFilePath(for key: String) -> String { - return "\(path)/\(makeFileName(for: key))" - } - - /// Calculates total disk cache size. - func totalSize() throws -> UInt64 { - var size: UInt64 = 0 - let contents = try fileManager.contentsOfDirectory(atPath: path) - for pathComponent in contents { - let filePath = NSString(string: path).appendingPathComponent(pathComponent) - let attributes = try fileManager.attributesOfItem(atPath: filePath) - if let fileSize = attributes[.size] as? UInt64 { - size += fileSize - } - } - return size - } - - func createDirectory() throws { - guard !fileManager.fileExists(atPath: path) else { - return - } - - try fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, - attributes: nil) - } - - /** - Removes objects if storage size exceeds max size. - - Parameter objects: Resource objects to remove - - Parameter totalSize: Total size - */ - func removeResourceObjects(_ objects: [ResourceObject], totalSize: UInt) throws { - guard config.maxSize > 0 && totalSize > config.maxSize else { - return - } - - var totalSize = totalSize - let targetSize = config.maxSize / 2 - - let sortedFiles = objects.sorted { - if let time1 = $0.resourceValues.contentModificationDate?.timeIntervalSinceReferenceDate, - let time2 = $1.resourceValues.contentModificationDate?.timeIntervalSinceReferenceDate { - return time1 > time2 - } else { - return false - } - } - - for file in sortedFiles { - try fileManager.removeItem(at: file.url) - if let fileSize = file.resourceValues.totalFileAllocatedSize { - totalSize -= UInt(fileSize) - } - if totalSize < targetSize { - break - } - } - } - - /** - Removes the object from the cache if it's expired. - - Parameter key: Unique key to identify the object in the cache - */ - func removeObjectIfExpired(forKey key: String) throws { - let filePath = makeFilePath(for: key) - let attributes = try fileManager.attributesOfItem(atPath: filePath) - if let expiryDate = attributes[.modificationDate] as? Date, expiryDate.inThePast { - try fileManager.removeItem(atPath: filePath) - } - } -} diff --git a/Source/Shared/Storage/DiskStorage2.swift b/Source/Shared/Storage/DiskStorage2.swift index 52193c0..6f64840 100644 --- a/Source/Shared/Storage/DiskStorage2.swift +++ b/Source/Shared/Storage/DiskStorage2.swift @@ -48,7 +48,7 @@ final class DiskStorage2: StorageAware2 { } extension DiskStorage2 { - func entry(forKey key: String) throws -> Entry2 { + func entry(forKey key: String) throws -> Entry { let filePath = makeFilePath(for: key) let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) let attributes = try fileManager.attributesOfItem(atPath: filePath) @@ -62,7 +62,7 @@ extension DiskStorage2 { "filePath": filePath ] - return Entry2( + return Entry( object: object, expiry: Expiry.date(date), meta: meta @@ -93,7 +93,7 @@ extension DiskStorage2 { .contentModificationDateKey, .totalFileAllocatedSizeKey ] - var resourceObjects = [ResourceObject2]() + var resourceObjects = [ResourceObject]() var filesToDelete = [URL]() var totalSize: UInt = 0 let fileEnumerator = fileManager.enumerator( @@ -144,7 +144,7 @@ extension DiskStorage2 { } } -typealias ResourceObject2 = (url: Foundation.URL, resourceValues: URLResourceValues) +typealias ResourceObject = (url: Foundation.URL, resourceValues: URLResourceValues) extension DiskStorage2 { /** diff --git a/Source/Shared/Storage/HybridStorage.swift b/Source/Shared/Storage/HybridStorage.swift deleted file mode 100644 index efd0477..0000000 --- a/Source/Shared/Storage/HybridStorage.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Foundation - -/// Use both memory and disk storage. Try on memory first. -class HybridStorage { - let memoryStorage: MemoryStorage - let diskStorage: DiskStorage - - init(memoryStorage: MemoryStorage, diskStorage: DiskStorage) { - self.memoryStorage = memoryStorage - self.diskStorage = diskStorage - } -} - -extension HybridStorage: StorageAware { - func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - do { - return try memoryStorage.entry(ofType: type, forKey: key) - } catch { - let entry = try diskStorage.entry(ofType: type, forKey: key) - // set back to memoryStorage - memoryStorage.setObject(entry.object, forKey: key, expiry: entry.expiry) - return entry - } - } - - func removeObject(forKey key: String) throws { - memoryStorage.removeObject(forKey: key) - try diskStorage.removeObject(forKey: key) - } - - func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { - memoryStorage.setObject(object, forKey: key, expiry: expiry) - try diskStorage.setObject(object, forKey: key, expiry: expiry) - } - - func removeAll() throws { - memoryStorage.removeAll() - try diskStorage.removeAll() - } - - func removeExpiredObjects() throws { - memoryStorage.removeExpiredObjects() - try diskStorage.removeExpiredObjects() - } -} diff --git a/Source/Shared/Storage/HybridStorage2.swift b/Source/Shared/Storage/HybridStorage2.swift index 05d27ad..6bbdb90 100644 --- a/Source/Shared/Storage/HybridStorage2.swift +++ b/Source/Shared/Storage/HybridStorage2.swift @@ -12,7 +12,7 @@ public class HybridStorage2 { } extension HybridStorage2: StorageAware2 { - public func entry(forKey key: String) throws -> Entry2 { + public func entry(forKey key: String) throws -> Entry { do { return try memoryStorage.entry(forKey: key) } catch { diff --git a/Source/Shared/Storage/MemoryStorage.swift b/Source/Shared/Storage/MemoryStorage.swift deleted file mode 100644 index 43f5be1..0000000 --- a/Source/Shared/Storage/MemoryStorage.swift +++ /dev/null @@ -1,86 +0,0 @@ -import UIKit - -/// Save objects to memory based on NSCache -final class MemoryStorage { - /// Memory cache instance - fileprivate let cache = NSCache() - // Memory cache keys - fileprivate var keys = Set() - /// Configuration - fileprivate let config: MemoryConfig - - // MARK: - Initialization - - init(config: MemoryConfig) { - self.config = config - self.cache.countLimit = Int(config.countLimit) - } -} - -extension MemoryStorage: StorageAware { - func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - guard let capsule = cache.object(forKey: NSString(string: key)) else { - throw StorageError.notFound - } - - guard let object = capsule.object as? T else { - throw StorageError.typeNotMatch - } - - return Entry(object: object, expiry: capsule.expiry) - } - - func removeObject(forKey key: String) { - cache.removeObject(forKey: NSString(string: key)) - keys.remove(key) - } - - func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) { - let capsule = MemoryCapsule(value: object, expiry: .date(expiry?.date ?? config.expiry.date)) - cache.setObject(capsule, forKey: NSString(string: key)) - keys.insert(key) - } - - func removeAll() { - cache.removeAllObjects() - keys.removeAll() - } - - func removeExpiredObjects() { - let allKeys = keys - for key in allKeys { - removeObjectIfExpired(forKey: key) - } - } -} - -extension MemoryStorage { - /** - Removes the object from the cache if it's expired. - - Parameter key: Unique key to identify the object in the cache - */ - func removeObjectIfExpired(forKey key: String) { - if let capsule = cache.object(forKey: NSString(string: key)), capsule.expiry.isExpired { - removeObject(forKey: key) - } - } -} - -/// Helper class to hold cached instance and expiry date. -/// Used in memory storage to work with NSCache. -class MemoryCapsule: NSObject { - /// Object to be cached - let object: Any - /// Expiration date - let expiry: Expiry - - /** - Creates a new instance of Capsule. - - Parameter value: Object to be cached - - Parameter expiry: Expiration date - */ - init(value: Any, expiry: Expiry) { - self.object = value - self.expiry = expiry - } -} diff --git a/Source/Shared/Storage/MemoryStorage2.swift b/Source/Shared/Storage/MemoryStorage2.swift index df2baf8..8ad494a 100644 --- a/Source/Shared/Storage/MemoryStorage2.swift +++ b/Source/Shared/Storage/MemoryStorage2.swift @@ -43,7 +43,7 @@ extension MemoryStorage2 { keys.remove(key) } - public func entry(forKey key: String) throws -> Entry2 { + public func entry(forKey key: String) throws -> Entry { guard let capsule = cache.object(forKey: NSString(string: key)) else { throw StorageError.notFound } @@ -52,7 +52,7 @@ extension MemoryStorage2 { throw StorageError.typeNotMatch } - return Entry2(object: object, expiry: capsule.expiry) + return Entry(object: object, expiry: capsule.expiry) } } diff --git a/Source/Shared/Storage/Storage.swift b/Source/Shared/Storage/Storage.swift deleted file mode 100644 index 1d71a08..0000000 --- a/Source/Shared/Storage/Storage.swift +++ /dev/null @@ -1,65 +0,0 @@ -import Foundation -import Dispatch - -/// Manage storage. Use memory storage if specified. -/// Synchronous by default. Use `async` for asynchronous operations. -public class Storage { - /// Used for sync operations - fileprivate let sync: StorageAware - - /// Storage used internally by both sync and async storages - private let interalStorage: StorageAware - - /// Initialize storage with configuration options. - /// - /// - Parameters: - /// - diskConfig: Configuration for disk storage - /// - memoryConfig: Optional. Pass confi if you want memory cache - /// - Throws: Throw StorageError if any. - public required init(diskConfig: DiskConfig, memoryConfig: MemoryConfig? = nil) throws { - // Disk or Hybrid - let storage: StorageAware - let disk = try DiskStorage(config: diskConfig) - - if let memoryConfig = memoryConfig { - let memory = MemoryStorage(config: memoryConfig) - storage = HybridStorage(memoryStorage: memory, diskStorage: disk) - } else { - storage = disk - } - - // Wrapper - self.interalStorage = TypeWrapperStorage(storage: storage) - - // Sync - self.sync = SyncStorage(storage: interalStorage, - serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")) - } - - /// Used for async operations - public lazy var async: AsyncStorage = AsyncStorage(storage: self.interalStorage, - serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")) -} - -extension Storage: StorageAware { - public func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - return try self.sync.entry(ofType: type, forKey: key) - } - - public func removeObject(forKey key: String) throws { - try self.sync.removeObject(forKey: key) - } - - public func setObject(_ object: T, forKey key: String, - expiry: Expiry? = nil) throws { - try self.sync.setObject(object, forKey: key, expiry: expiry) - } - - public func removeAll() throws { - try self.sync.removeAll() - } - - public func removeExpiredObjects() throws { - try self.sync.removeExpiredObjects() - } -} diff --git a/Source/Shared/Storage/Storage2.swift b/Source/Shared/Storage/Storage2.swift index 60f7b9c..139f0e7 100644 --- a/Source/Shared/Storage/Storage2.swift +++ b/Source/Shared/Storage/Storage2.swift @@ -46,7 +46,7 @@ public class Storage2 { } extension Storage2: StorageAware2 { - public func entry(forKey key: String) throws -> Entry2 { + public func entry(forKey key: String) throws -> Entry { return try self.syncStorage.entry(forKey: key) } diff --git a/Source/Shared/Storage/StorageAware.swift b/Source/Shared/Storage/StorageAware.swift deleted file mode 100644 index 3096f09..0000000 --- a/Source/Shared/Storage/StorageAware.swift +++ /dev/null @@ -1,78 +0,0 @@ -import Foundation - -/// A protocol used for saving and loading from storage -public protocol StorageAware { - /** - Tries to retrieve the object from the storage. - - Parameter key: Unique key to identify the object in the cache - - Returns: Cached object or nil if not found - */ - func object(ofType type: T.Type, forKey key: String) throws -> T - - /** - Get cache entry which includes object with metadata. - - Parameter key: Unique key to identify the object in the cache - - Returns: Object wrapper with metadata or nil if not found - */ - func entry(ofType type: T.Type, forKey key: String) throws -> Entry - - /** - Removes the object by the given key. - - Parameter key: Unique key to identify the object. - */ - func removeObject(forKey key: String) throws - - /** - Saves passed object. - - Parameter key: Unique key to identify the object in the cache. - - Parameter object: Object that needs to be cached. - - Parameter expiry: Overwrite expiry for this object only. - */ - func setObject(_ object: T, forKey key: String, expiry: Expiry?) throws - - /** - Check if an object exist by the given key. - - Parameter key: Unique key to identify the object. - */ - func existsObject(ofType type: T.Type, forKey key: String) throws -> Bool - - /** - Removes all objects from the cache storage. - */ - func removeAll() throws - - /** - Clears all expired objects. - */ - func removeExpiredObjects() throws - - /** - Check if an expired object by the given key. - - Parameter key: Unique key to identify the object. - */ - func isExpiredObject(ofType type: T.Type, forKey key: String) throws -> Bool -} - -public extension StorageAware { - func object(ofType type: T.Type, forKey key: String) throws -> T { - return try entry(ofType: type, forKey: key).object - } - - func existsObject(ofType type: T.Type, forKey key: String) throws -> Bool { - do { - let _: T = try object(ofType: type, forKey: key) - return true - } catch { - return false - } - } - - func isExpiredObject(ofType type: T.Type, forKey key: String) throws -> Bool { - do { - let entry = try self.entry(ofType: type, forKey: key) - return entry.expiry.isExpired - } catch { - return true - } - } -} diff --git a/Source/Shared/Storage/StorageAware2.swift b/Source/Shared/Storage/StorageAware2.swift index 62bd13c..4659de6 100644 --- a/Source/Shared/Storage/StorageAware2.swift +++ b/Source/Shared/Storage/StorageAware2.swift @@ -15,7 +15,7 @@ public protocol StorageAware2 { - Parameter key: Unique key to identify the object in the cache - Returns: Object wrapper with metadata or nil if not found */ - func entry(forKey key: String) throws -> Entry2 + func entry(forKey key: String) throws -> Entry /** Removes the object by the given key. diff --git a/Source/Shared/Storage/SyncStorage.swift b/Source/Shared/Storage/SyncStorage.swift deleted file mode 100644 index 30c6867..0000000 --- a/Source/Shared/Storage/SyncStorage.swift +++ /dev/null @@ -1,50 +0,0 @@ -import Foundation -import Dispatch - -/// Manipulate storage in a "all sync" manner. -/// Block the current queue until the operation completes. -public class SyncStorage { - fileprivate let internalStorage: StorageAware - fileprivate let serialQueue: DispatchQueue - - init(storage: StorageAware, serialQueue: DispatchQueue) { - self.internalStorage = storage - self.serialQueue = serialQueue - } -} - -extension SyncStorage: StorageAware { - public func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - var entry: Entry! - try serialQueue.sync { - entry = try internalStorage.entry(ofType: type, forKey: key) - } - - return entry - } - - public func removeObject(forKey key: String) throws { - try serialQueue.sync { - try self.internalStorage.removeObject(forKey: key) - } - } - - public func setObject(_ object: T, forKey key: String, - expiry: Expiry? = nil) throws { - try serialQueue.sync { - try internalStorage.setObject(object, forKey: key, expiry: expiry) - } - } - - public func removeAll() throws { - try serialQueue.sync { - try internalStorage.removeAll() - } - } - - public func removeExpiredObjects() throws { - try serialQueue.sync { - try internalStorage.removeExpiredObjects() - } - } -} diff --git a/Source/Shared/Storage/SyncStorage2.swift b/Source/Shared/Storage/SyncStorage2.swift index f43119a..9c1c217 100644 --- a/Source/Shared/Storage/SyncStorage2.swift +++ b/Source/Shared/Storage/SyncStorage2.swift @@ -14,8 +14,8 @@ public class SyncStorage2 { } extension SyncStorage2: StorageAware2 { - public func entry(forKey key: String) throws -> Entry2 { - var entry: Entry2! + public func entry(forKey key: String) throws -> Entry { + var entry: Entry! try serialQueue.sync { entry = try innerStorage.entry(forKey: key) } diff --git a/Source/Shared/Storage/TypeWrapperStorage.swift b/Source/Shared/Storage/TypeWrapperStorage.swift deleted file mode 100644 index cabbd4a..0000000 --- a/Source/Shared/Storage/TypeWrapperStorage.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -/// Deal with top level primitive. Use TypeWrapper as wrapper -/// Because we use `JSONEncoder` and `JSONDecoder`. -/// Avoid issue like "Top-level T encoded as number JSON fragment" -final class TypeWrapperStorage { - let internalStorage: StorageAware - - init(storage: StorageAware) { - self.internalStorage = storage - } -} - -extension TypeWrapperStorage: StorageAware { - public func entry(ofType type: T.Type, forKey key: String) throws -> Entry { - let wrapperEntry = try internalStorage.entry(ofType: TypeWrapper.self, forKey: key) - return Entry(object: wrapperEntry.object.object, expiry: wrapperEntry.expiry, meta: wrapperEntry.meta) - } - - public func removeObject(forKey key: String) throws { - try internalStorage.removeObject(forKey: key) - } - - public func setObject(_ object: T, forKey key: String, - expiry: Expiry? = nil) throws { - let wrapper = TypeWrapper(object: object) - try internalStorage.setObject(wrapper, forKey: key, expiry: expiry) - } - - public func removeAll() throws { - try internalStorage.removeAll() - } - - public func removeExpiredObjects() throws { - try internalStorage.removeExpiredObjects() - } -} From 6e468a110d4adc339f58a69116f0b9d5d46b4e7e Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:15:16 +0200 Subject: [PATCH 14/31] Rename to Storage --- Cache.xcodeproj/project.pbxproj | 112 +++++++++--------- ...AsyncStorage2.swift => AsyncStorage.swift} | 14 +-- .../{DiskStorage2.swift => DiskStorage.swift} | 14 +-- ...bridStorage2.swift => HybridStorage.swift} | 16 +-- ...moryStorage2.swift => MemoryStorage.swift} | 10 +- Source/Shared/Storage/Storage+Support.swift | 8 +- .../Storage/{Storage2.swift => Storage.swift} | 26 ++-- ...StorageAware2.swift => StorageAware.swift} | 0 .../{SyncStorage2.swift => SyncStorage.swift} | 14 +-- 9 files changed, 107 insertions(+), 107 deletions(-) rename Source/Shared/Storage/{AsyncStorage2.swift => AsyncStorage.swift} (90%) rename Source/Shared/Storage/{DiskStorage2.swift => DiskStorage.swift} (96%) rename Source/Shared/Storage/{HybridStorage2.swift => HybridStorage.swift} (77%) rename Source/Shared/Storage/{MemoryStorage2.swift => MemoryStorage.swift} (88%) rename Source/Shared/Storage/{Storage2.swift => Storage.swift} (74%) rename Source/Shared/Storage/{StorageAware2.swift => StorageAware.swift} (100%) rename Source/Shared/Storage/{SyncStorage2.swift => SyncStorage.swift} (78%) diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index e8373bc..dc89381 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -27,34 +27,34 @@ D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984D1F694FFA00CE8F68 /* DiskConfig.swift */; }; D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */; }; D21B66A11F6A747000125DE1 /* ImageWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98761F69513800CE8F68 /* ImageWrapperTests.swift */; }; - D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; - D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; - D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */; }; + D221E5BC20D00D9300BC940E /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage.swift */; }; + D221E5BD20D00D9300BC940E /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage.swift */; }; + D221E5BE20D00D9300BC940E /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage.swift */; }; D221E5C020D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; }; D221E5C120D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; }; D221E5C220D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; }; - D221E5C420D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; - D221E5C520D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; - D221E5C620D00DDB00BC940E /* DiskStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage2.swift */; }; + D221E5C420D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; }; + D221E5C520D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; }; + D221E5C620D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; }; D236F31A1F6BEF73004EE01F /* StorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236F3191F6BEF73004EE01F /* StorageTests.swift */; }; D270147020D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; D270147120D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; D270147220D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; - D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; - D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; - D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware2.swift */; }; - D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; - D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; - D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage2.swift */; }; - D270147C20D107DA003B45C7 /* SyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage2.swift */; }; - D270147D20D107DA003B45C7 /* SyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage2.swift */; }; - D270147E20D107DA003B45C7 /* SyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage2.swift */; }; - D270148020D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; - D270148120D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; - D270148220D10982003B45C7 /* Storage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage2.swift */; }; - D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; - D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; - D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage2.swift */; }; + D270147420D101F3003B45C7 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware.swift */; }; + D270147520D101F3003B45C7 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware.swift */; }; + D270147620D101F3003B45C7 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware.swift */; }; + D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage.swift */; }; + D270147920D1046A003B45C7 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage.swift */; }; + D270147A20D1046A003B45C7 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage.swift */; }; + D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage.swift */; }; + D270147D20D107DA003B45C7 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage.swift */; }; + D270147E20D107DA003B45C7 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage.swift */; }; + D270148020D10982003B45C7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage.swift */; }; + D270148120D10982003B45C7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage.swift */; }; + D270148220D10982003B45C7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage.swift */; }; + D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; }; + D270148520D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; }; + D270148620D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; }; D270148820D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; D270148920D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; @@ -156,16 +156,16 @@ /* Begin PBXFileReference section */ BDEDD3561DBCE5B1007416A6 /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BDEDD3781DBCEB8A007416A6 /* Cache-tvOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cache-tvOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage2.swift; sourceTree = ""; }; + D221E5BB20D00D9300BC940E /* MemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = ""; }; D221E5BF20D00DCC00BC940E /* Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entry.swift; sourceTree = ""; }; - D221E5C320D00DDB00BC940E /* DiskStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage2.swift; sourceTree = ""; }; + D221E5C320D00DDB00BC940E /* DiskStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage.swift; sourceTree = ""; }; D236F3191F6BEF73004EE01F /* StorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageTests.swift; sourceTree = ""; }; D270146F20D1018A003B45C7 /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = ""; }; - D270147320D101F3003B45C7 /* StorageAware2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAware2.swift; sourceTree = ""; }; - D270147720D1046A003B45C7 /* HybridStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridStorage2.swift; sourceTree = ""; }; - D270147B20D107DA003B45C7 /* SyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage2.swift; sourceTree = ""; }; - D270147F20D10982003B45C7 /* Storage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage2.swift; sourceTree = ""; }; - D270148320D10E76003B45C7 /* AsyncStorage2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage2.swift; sourceTree = ""; }; + D270147320D101F3003B45C7 /* StorageAware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAware.swift; sourceTree = ""; }; + D270147720D1046A003B45C7 /* HybridStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridStorage.swift; sourceTree = ""; }; + D270147B20D107DA003B45C7 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; + D270147F20D10982003B45C7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; + D270148320D10E76003B45C7 /* AsyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage.swift; sourceTree = ""; }; D270148720D11040003B45C7 /* Storage+Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Support.swift"; sourceTree = ""; }; D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; D270148F20D1251E003B45C7 /* TypeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapper.swift; sourceTree = ""; }; @@ -330,13 +330,13 @@ D2CF985A1F694FFA00CE8F68 /* Storage */ = { isa = PBXGroup; children = ( - D221E5BB20D00D9300BC940E /* MemoryStorage2.swift */, - D221E5C320D00DDB00BC940E /* DiskStorage2.swift */, - D270147320D101F3003B45C7 /* StorageAware2.swift */, - D270147720D1046A003B45C7 /* HybridStorage2.swift */, - D270147B20D107DA003B45C7 /* SyncStorage2.swift */, - D270147F20D10982003B45C7 /* Storage2.swift */, - D270148320D10E76003B45C7 /* AsyncStorage2.swift */, + D221E5BB20D00D9300BC940E /* MemoryStorage.swift */, + D221E5C320D00DDB00BC940E /* DiskStorage.swift */, + D270147320D101F3003B45C7 /* StorageAware.swift */, + D270147720D1046A003B45C7 /* HybridStorage.swift */, + D270147B20D107DA003B45C7 /* SyncStorage.swift */, + D270147F20D10982003B45C7 /* Storage.swift */, + D270148320D10E76003B45C7 /* AsyncStorage.swift */, D270148720D11040003B45C7 /* Storage+Support.swift */, ); path = Storage; @@ -782,21 +782,21 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D221E5C620D00DDB00BC940E /* DiskStorage2.swift in Sources */, + D221E5C620D00DDB00BC940E /* DiskStorage.swift in Sources */, D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */, D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */, D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */, - D270147620D101F3003B45C7 /* StorageAware2.swift in Sources */, + D270147620D101F3003B45C7 /* StorageAware.swift in Sources */, D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */, D270149220D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */, - D221E5BE20D00D9300BC940E /* MemoryStorage2.swift in Sources */, + D221E5BE20D00D9300BC940E /* MemoryStorage.swift in Sources */, D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D270147220D1018A003B45C7 /* Transformer.swift in Sources */, D21B668C1F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, - D270147E20D107DA003B45C7 /* SyncStorage2.swift in Sources */, + D270147E20D107DA003B45C7 /* SyncStorage.swift in Sources */, D292DAFF1F6A970B0060F614 /* Result.swift in Sources */, D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */, D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */, @@ -804,11 +804,11 @@ D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */, D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, - D270147A20D1046A003B45C7 /* HybridStorage2.swift in Sources */, - D270148620D10E76003B45C7 /* AsyncStorage2.swift in Sources */, + D270147A20D1046A003B45C7 /* HybridStorage.swift in Sources */, + D270148620D10E76003B45C7 /* AsyncStorage.swift in Sources */, D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */, D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, - D270148220D10982003B45C7 /* Storage2.swift in Sources */, + D270148220D10982003B45C7 /* Storage.swift in Sources */, D221E5C220D00DCC00BC940E /* Entry.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -855,21 +855,21 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D221E5C520D00DDB00BC940E /* DiskStorage2.swift in Sources */, + D221E5C520D00DDB00BC940E /* DiskStorage.swift in Sources */, D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */, D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */, D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */, - D270147520D101F3003B45C7 /* StorageAware2.swift in Sources */, + D270147520D101F3003B45C7 /* StorageAware.swift in Sources */, D21B669C1F6A724600125DE1 /* MemoryConfig.swift in Sources */, D270149120D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */, - D221E5BD20D00D9300BC940E /* MemoryStorage2.swift in Sources */, + D221E5BD20D00D9300BC940E /* MemoryStorage.swift in Sources */, D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D270147120D1018A003B45C7 /* Transformer.swift in Sources */, D21B66831F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, - D270147D20D107DA003B45C7 /* SyncStorage2.swift in Sources */, + D270147D20D107DA003B45C7 /* SyncStorage.swift in Sources */, D292DAFE1F6A970B0060F614 /* Result.swift in Sources */, D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */, D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */, @@ -877,11 +877,11 @@ D270148920D11040003B45C7 /* Storage+Support.swift in Sources */, D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */, D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, - D270147920D1046A003B45C7 /* HybridStorage2.swift in Sources */, - D270148520D10E76003B45C7 /* AsyncStorage2.swift in Sources */, + D270147920D1046A003B45C7 /* HybridStorage.swift in Sources */, + D270148520D10E76003B45C7 /* AsyncStorage.swift in Sources */, D270149520D125AC003B45C7 /* MemoryCapsule.swift in Sources */, D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, - D270148120D10982003B45C7 /* Storage2.swift in Sources */, + D270148120D10982003B45C7 /* Storage.swift in Sources */, D221E5C120D00DCC00BC940E /* Entry.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -906,30 +906,30 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D221E5C420D00DDB00BC940E /* DiskStorage2.swift in Sources */, + D221E5C420D00DDB00BC940E /* DiskStorage.swift in Sources */, D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */, D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */, D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */, D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */, - D270147420D101F3003B45C7 /* StorageAware2.swift in Sources */, + D270147420D101F3003B45C7 /* StorageAware.swift in Sources */, D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */, D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */, D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */, - D221E5BC20D00D9300BC940E /* MemoryStorage2.swift in Sources */, + D221E5BC20D00D9300BC940E /* MemoryStorage.swift in Sources */, D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */, D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */, D270147020D1018A003B45C7 /* Transformer.swift in Sources */, D292DAFD1F6A970B0060F614 /* Result.swift in Sources */, D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, - D270147C20D107DA003B45C7 /* SyncStorage2.swift in Sources */, + D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */, D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */, D270148820D11040003B45C7 /* Storage+Support.swift in Sources */, D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */, D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, - D270147820D1046A003B45C7 /* HybridStorage2.swift in Sources */, - D270148420D10E76003B45C7 /* AsyncStorage2.swift in Sources */, + D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */, + D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */, D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */, - D270148020D10982003B45C7 /* Storage2.swift in Sources */, + D270148020D10982003B45C7 /* Storage.swift in Sources */, D270149420D125AC003B45C7 /* MemoryCapsule.swift in Sources */, D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */, D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */, diff --git a/Source/Shared/Storage/AsyncStorage2.swift b/Source/Shared/Storage/AsyncStorage.swift similarity index 90% rename from Source/Shared/Storage/AsyncStorage2.swift rename to Source/Shared/Storage/AsyncStorage.swift index bbda340..f2ca945 100644 --- a/Source/Shared/Storage/AsyncStorage2.swift +++ b/Source/Shared/Storage/AsyncStorage.swift @@ -3,17 +3,17 @@ import Dispatch /// Manipulate storage in a "all async" manner. /// The completion closure will be called when operation completes. -public class AsyncStorage2 { - fileprivate let innerStorage: HybridStorage2 +public class AsyncStorage { + fileprivate let innerStorage: HybridStorage public let serialQueue: DispatchQueue - init(storage: HybridStorage2, serialQueue: DispatchQueue) { + init(storage: HybridStorage, serialQueue: DispatchQueue) { self.innerStorage = storage self.serialQueue = serialQueue } } -extension AsyncStorage2 { +extension AsyncStorage { public func entry(forKey key: String, completion: @escaping (Result>) -> Void) { serialQueue.async { [weak self] in guard let `self` = self else { @@ -117,9 +117,9 @@ extension AsyncStorage2 { } } -public extension AsyncStorage2 { - func support(transformer: Transformer) -> AsyncStorage2 { - let storage = AsyncStorage2( +public extension AsyncStorage { + func support(transformer: Transformer) -> AsyncStorage { + let storage = AsyncStorage( storage: innerStorage.support(transformer: transformer), serialQueue: serialQueue ) diff --git a/Source/Shared/Storage/DiskStorage2.swift b/Source/Shared/Storage/DiskStorage.swift similarity index 96% rename from Source/Shared/Storage/DiskStorage2.swift rename to Source/Shared/Storage/DiskStorage.swift index 6f64840..8b14644 100644 --- a/Source/Shared/Storage/DiskStorage2.swift +++ b/Source/Shared/Storage/DiskStorage.swift @@ -1,7 +1,7 @@ import Foundation /// Save objects to file on disk -final class DiskStorage2: StorageAware2 { +final class DiskStorage: StorageAware2 { enum Error: Swift.Error { case fileEnumeratorFailed } @@ -47,7 +47,7 @@ final class DiskStorage2: StorageAware2 { } } -extension DiskStorage2 { +extension DiskStorage { func entry(forKey key: String) throws -> Entry { let filePath = makeFilePath(for: key) let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) @@ -134,7 +134,7 @@ extension DiskStorage2 { } } -extension DiskStorage2 { +extension DiskStorage { /** Sets attributes on the disk cache folder. - Parameter attributes: Directory attributes @@ -146,7 +146,7 @@ extension DiskStorage2 { typealias ResourceObject = (url: Foundation.URL, resourceValues: URLResourceValues) -extension DiskStorage2 { +extension DiskStorage { /** Builds file name from the key. - Parameter key: Unique key to identify the object in the cache @@ -234,10 +234,10 @@ extension DiskStorage2 { } } -extension DiskStorage2 { - func support(transformer: Transformer) -> DiskStorage2 { +extension DiskStorage { + func support(transformer: Transformer) -> DiskStorage { // swiftlint:disable force_try - let storage = try! DiskStorage2( + let storage = try! DiskStorage( config: config, fileManager: fileManager, transformer: transformer diff --git a/Source/Shared/Storage/HybridStorage2.swift b/Source/Shared/Storage/HybridStorage.swift similarity index 77% rename from Source/Shared/Storage/HybridStorage2.swift rename to Source/Shared/Storage/HybridStorage.swift index 6bbdb90..0994329 100644 --- a/Source/Shared/Storage/HybridStorage2.swift +++ b/Source/Shared/Storage/HybridStorage.swift @@ -1,17 +1,17 @@ import Foundation /// Use both memory and disk storage. Try on memory first. -public class HybridStorage2 { - let memoryStorage: MemoryStorage2 - let diskStorage: DiskStorage2 +public class HybridStorage { + let memoryStorage: MemoryStorage + let diskStorage: DiskStorage - init(memoryStorage: MemoryStorage2, diskStorage: DiskStorage2) { + init(memoryStorage: MemoryStorage, diskStorage: DiskStorage) { self.memoryStorage = memoryStorage self.diskStorage = diskStorage } } -extension HybridStorage2: StorageAware2 { +extension HybridStorage: StorageAware2 { public func entry(forKey key: String) throws -> Entry { do { return try memoryStorage.entry(forKey: key) @@ -44,9 +44,9 @@ extension HybridStorage2: StorageAware2 { } } -public extension HybridStorage2 { - func support(transformer: Transformer) -> HybridStorage2 { - let storage = HybridStorage2( +public extension HybridStorage { + func support(transformer: Transformer) -> HybridStorage { + let storage = HybridStorage( memoryStorage: memoryStorage.support(), diskStorage: diskStorage.support(transformer: transformer) ) diff --git a/Source/Shared/Storage/MemoryStorage2.swift b/Source/Shared/Storage/MemoryStorage.swift similarity index 88% rename from Source/Shared/Storage/MemoryStorage2.swift rename to Source/Shared/Storage/MemoryStorage.swift index 8ad494a..1622dd0 100644 --- a/Source/Shared/Storage/MemoryStorage2.swift +++ b/Source/Shared/Storage/MemoryStorage.swift @@ -1,6 +1,6 @@ import Foundation -public class MemoryStorage2: StorageAware2 { +public class MemoryStorage: StorageAware2 { fileprivate let cache = NSCache() // Memory cache keys fileprivate var keys = Set() @@ -13,7 +13,7 @@ public class MemoryStorage2: StorageAware2 { } } -extension MemoryStorage2 { +extension MemoryStorage { public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) { let capsule = MemoryCapsule(value: object, expiry: .date(expiry?.date ?? config.expiry.date)) cache.setObject(capsule, forKey: NSString(string: key)) @@ -56,9 +56,9 @@ extension MemoryStorage2 { } } -public extension MemoryStorage2 { - func support() -> MemoryStorage2 { - let storage = MemoryStorage2(config: config) +public extension MemoryStorage { + func support() -> MemoryStorage { + let storage = MemoryStorage(config: config) return storage } } diff --git a/Source/Shared/Storage/Storage+Support.swift b/Source/Shared/Storage/Storage+Support.swift index 74014e2..76468c2 100644 --- a/Source/Shared/Storage/Storage+Support.swift +++ b/Source/Shared/Storage/Storage+Support.swift @@ -1,17 +1,17 @@ import Foundation -public extension Storage2 { - func supportData() -> Storage2 { +public extension Storage { + func supportData() -> Storage { let storage = support(transformer: Transformer.forData()) return storage } - func supportImage() -> Storage2 { + func supportImage() -> Storage { let storage = support(transformer: Transformer.forImage()) return storage } - func supportCodable(ofType: U.Type) -> Storage2 { + func supportCodable(ofType: U.Type) -> Storage { let storage = support(transformer: Transformer.forCodable(ofType: U.self)) return storage } diff --git a/Source/Shared/Storage/Storage2.swift b/Source/Shared/Storage/Storage.swift similarity index 74% rename from Source/Shared/Storage/Storage2.swift rename to Source/Shared/Storage/Storage.swift index 139f0e7..d68b1bc 100644 --- a/Source/Shared/Storage/Storage2.swift +++ b/Source/Shared/Storage/Storage.swift @@ -3,10 +3,10 @@ import Dispatch /// Manage storage. Use memory storage if specified. /// Synchronous by default. Use `async` for asynchronous operations. -public class Storage2 { +public class Storage { /// Used for sync operations - let syncStorage: SyncStorage2 - let asyncStorage: AsyncStorage2 + let syncStorage: SyncStorage + let asyncStorage: AsyncStorage /// Initialize storage with configuration options. /// @@ -15,16 +15,16 @@ public class Storage2 { /// - memoryConfig: Optional. Pass config if you want memory cache /// - Throws: Throw StorageError if any. public convenience init(diskConfig: DiskConfig, memoryConfig: MemoryConfig, transformer: Transformer) throws { - let disk = try DiskStorage2(config: diskConfig, transformer: transformer) - let memory = MemoryStorage2(config: memoryConfig) + let disk = try DiskStorage(config: diskConfig, transformer: transformer) + let memory = MemoryStorage(config: memoryConfig) - let hybridStorage = HybridStorage2(memoryStorage: memory, diskStorage: disk) - let syncStorage = SyncStorage2( + let hybridStorage = HybridStorage(memoryStorage: memory, diskStorage: disk) + let syncStorage = SyncStorage( innerStorage: hybridStorage, serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue") ) - let asyncStorage = AsyncStorage2( + let asyncStorage = AsyncStorage( storage: hybridStorage, serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue") ) @@ -36,7 +36,7 @@ public class Storage2 { /// /// - Parameter syncStorage: Synchronous storage /// - Paraeter: asyncStorage: Asynchronous storage - public required init(syncStorage: SyncStorage2, asyncStorage: AsyncStorage2) { + public required init(syncStorage: SyncStorage, asyncStorage: AsyncStorage) { self.syncStorage = syncStorage self.asyncStorage = asyncStorage } @@ -45,7 +45,7 @@ public class Storage2 { public lazy var async = self.asyncStorage } -extension Storage2: StorageAware2 { +extension Storage: StorageAware2 { public func entry(forKey key: String) throws -> Entry { return try self.syncStorage.entry(forKey: key) } @@ -67,9 +67,9 @@ extension Storage2: StorageAware2 { } } -public extension Storage2 { - func support(transformer: Transformer) -> Storage2 { - let storage = Storage2( +public extension Storage { + func support(transformer: Transformer) -> Storage { + let storage = Storage( syncStorage: syncStorage.support(transformer: transformer), asyncStorage: asyncStorage.support(transformer: transformer) ) diff --git a/Source/Shared/Storage/StorageAware2.swift b/Source/Shared/Storage/StorageAware.swift similarity index 100% rename from Source/Shared/Storage/StorageAware2.swift rename to Source/Shared/Storage/StorageAware.swift diff --git a/Source/Shared/Storage/SyncStorage2.swift b/Source/Shared/Storage/SyncStorage.swift similarity index 78% rename from Source/Shared/Storage/SyncStorage2.swift rename to Source/Shared/Storage/SyncStorage.swift index 9c1c217..aeb158e 100644 --- a/Source/Shared/Storage/SyncStorage2.swift +++ b/Source/Shared/Storage/SyncStorage.swift @@ -3,17 +3,17 @@ import Dispatch /// Manipulate storage in a "all sync" manner. /// Block the current queue until the operation completes. -public class SyncStorage2 { - fileprivate let innerStorage: HybridStorage2 +public class SyncStorage { + fileprivate let innerStorage: HybridStorage fileprivate let serialQueue: DispatchQueue - init(innerStorage: HybridStorage2, serialQueue: DispatchQueue) { + init(innerStorage: HybridStorage, serialQueue: DispatchQueue) { self.innerStorage = innerStorage self.serialQueue = serialQueue } } -extension SyncStorage2: StorageAware2 { +extension SyncStorage: StorageAware2 { public func entry(forKey key: String) throws -> Entry { var entry: Entry! try serialQueue.sync { @@ -48,9 +48,9 @@ extension SyncStorage2: StorageAware2 { } } -public extension SyncStorage2 { - func support(transformer: Transformer) -> SyncStorage2 { - let storage = SyncStorage2( +public extension SyncStorage { + func support(transformer: Transformer) -> SyncStorage { + let storage = SyncStorage( innerStorage: innerStorage.support(transformer: transformer), serialQueue: serialQueue ) From d38c941084385fb44c3d126911a87bcc1e2aa639 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:29:03 +0200 Subject: [PATCH 15/31] Readd totalCostLimit --- Source/Shared/Configuration/MemoryConfig.swift | 18 ++++++++---------- Source/Shared/Storage/MemoryStorage.swift | 1 + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Source/Shared/Configuration/MemoryConfig.swift b/Source/Shared/Configuration/MemoryConfig.swift index 62bdfbd..898e603 100644 --- a/Source/Shared/Configuration/MemoryConfig.swift +++ b/Source/Shared/Configuration/MemoryConfig.swift @@ -4,19 +4,17 @@ public struct MemoryConfig { /// Expiry date that will be applied by default for every added object /// if it's not overridden in the add(key: object: expiry: completion:) method public let expiry: Expiry - /// The maximum number of objects in memory the cache should hold. 0 means no limit. + /// The maximum number of objects in memory the cache should hold. + /// If 0, there is no count limit. The default value is 0. public let countLimit: UInt - public init(expiry: Expiry = .never, countLimit: UInt = 0) { + /// The maximum total cost that the cache can hold before it starts evicting objects. + /// If 0, there is no total cost limit. The default value is 0 + public let totalCostLimit: UInt + + public init(expiry: Expiry = .never, countLimit: UInt = 0, totalCostLimit: UInt = 0) { self.expiry = expiry self.countLimit = countLimit - } - - // MARK: - Deprecated - @available(*, deprecated, - message: "Use init(expiry:countLimit:) instead.", - renamed: "init(expiry:countLimit:)") - public init(expiry: Expiry = .never, countLimit: UInt = 0, totalCostLimit: UInt = 0) { - self.init(expiry: expiry, countLimit: countLimit) + self.totalCostLimit = totalCostLimit } } diff --git a/Source/Shared/Storage/MemoryStorage.swift b/Source/Shared/Storage/MemoryStorage.swift index 1622dd0..03b75e7 100644 --- a/Source/Shared/Storage/MemoryStorage.swift +++ b/Source/Shared/Storage/MemoryStorage.swift @@ -10,6 +10,7 @@ public class MemoryStorage: StorageAware2 { public init(config: MemoryConfig) { self.config = config self.cache.countLimit = Int(config.countLimit) + self.cache.totalCostLimit = Int(config.totalCostLimit) } } From be3a40cc01a88782940dee68b34137bebe6cf4a4 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:29:15 +0200 Subject: [PATCH 16/31] Update MemoryStorageTests --- Cache.xcodeproj/project.pbxproj | 22 +++----------- .../Tests/Library/ObjectConverterTests.swift | 13 ++++---- .../Tests/Storage/MemoryStorageTests.swift | 30 +++++++++---------- 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index dc89381..945ecb7 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -36,7 +36,6 @@ D221E5C420D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; }; D221E5C520D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; }; D221E5C620D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; }; - D236F31A1F6BEF73004EE01F /* StorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236F3191F6BEF73004EE01F /* StorageTests.swift */; }; D270147020D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; D270147120D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; D270147220D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; }; @@ -67,6 +66,8 @@ D270149420D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; D270149520D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; + D270149E20D1286F003B45C7 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; }; + D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -78,10 +79,6 @@ D292DAFD1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; }; D292DAFE1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; }; D292DAFF1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; }; - D292DB011F6AA06B0060F614 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; - D292DB021F6AA06B0060F614 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; - D292DB041F6AA0730060F614 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; }; - D292DB051F6AA0730060F614 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; }; D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF981B1F69427C00CE8F68 /* TestCase+Extensions.swift */; }; D2CF981F1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF981B1F69427C00CE8F68 /* TestCase+Extensions.swift */; }; D2CF98201F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF981B1F69427C00CE8F68 /* TestCase+Extensions.swift */; }; @@ -104,11 +101,7 @@ D2CF987E1F69513800CE8F68 /* ExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98751F69513800CE8F68 /* ExpiryTests.swift */; }; D2CF987F1F69513800CE8F68 /* ImageWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98761F69513800CE8F68 /* ImageWrapperTests.swift */; }; D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98771F69513800CE8F68 /* TypeWrapperTests.swift */; }; - D2CF98811F69513800CE8F68 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; }; - D2CF98821F69513800CE8F68 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; - D2CF98831F69513800CE8F68 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; }; D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; }; - D2CF98891F695F9400CE8F68 /* TypeWrapperStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */; }; D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; }; D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; }; D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; }; @@ -819,6 +812,7 @@ files = ( D2CF98201F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, D2CF98231F69427C00CE8F68 /* TestHelper.swift in Sources */, + D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */, D2CF98261F69427C00CE8F68 /* User.swift in Sources */, D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */, ); @@ -830,24 +824,18 @@ files = ( D2CF987E1F69513800CE8F68 /* ExpiryTests.swift in Sources */, D28A1D231F6FFEF50030DF81 /* ObjectConverterTests.swift in Sources */, - D2CF98891F695F9400CE8F68 /* TypeWrapperStorageTests.swift in Sources */, - D2CF98821F69513800CE8F68 /* HybridStorageTests.swift in Sources */, - D2CF98831F69513800CE8F68 /* MemoryStorageTests.swift in Sources */, + D270149E20D1286F003B45C7 /* MemoryStorageTests.swift in Sources */, D2CF987C1F69513800CE8F68 /* Date+ExtensionsTests.swift in Sources */, D28C9BAC1F67ECD400C180C1 /* TestHelper+iOS.swift in Sources */, D2CF98211F69427C00CE8F68 /* TestHelper.swift in Sources */, D2CF987F1F69513800CE8F68 /* ImageWrapperTests.swift in Sources */, D2D4CC1A1FA3166900E4A2D5 /* MD5Tests.swift in Sources */, - D292DB041F6AA0730060F614 /* AsyncStorageTests.swift in Sources */, D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */, D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */, D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */, D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */, D2CF98241F69427C00CE8F68 /* User.swift in Sources */, D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, - D236F31A1F6BEF73004EE01F /* StorageTests.swift in Sources */, - D292DB011F6AA06B0060F614 /* SyncStorageTests.swift in Sources */, - D2CF98811F69513800CE8F68 /* DiskStorageTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -890,7 +878,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D292DB051F6AA0730060F614 /* AsyncStorageTests.swift in Sources */, D28A1D221F6FFEF50030DF81 /* ObjectConverterTests.swift in Sources */, D2CF98221F69427C00CE8F68 /* TestHelper.swift in Sources */, D21B66A11F6A747000125DE1 /* ImageWrapperTests.swift in Sources */, @@ -898,7 +885,6 @@ D5291D851C283C7C00B702C9 /* TestHelper+OSX.swift in Sources */, D5291DA31C2841D200B702C9 /* NSImage+ExtensionsTests.swift in Sources */, D2CF981F1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, - D292DB021F6AA06B0060F614 /* SyncStorageTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/iOS/Tests/Library/ObjectConverterTests.swift b/Tests/iOS/Tests/Library/ObjectConverterTests.swift index f6b605b..66b2a5e 100644 --- a/Tests/iOS/Tests/Library/ObjectConverterTests.swift +++ b/Tests/iOS/Tests/Library/ObjectConverterTests.swift @@ -2,12 +2,15 @@ import XCTest @testable import Cache final class JSONDecoderExtensionsTests: XCTestCase { - private var storage: HybridStorage! + private var storage: HybridStorage! override func setUp() { super.setUp() - let memory = MemoryStorage(config: MemoryConfig()) - let disk = try! DiskStorage(config: DiskConfig(name: "HybridDisk")) + let memory = MemoryStorage(config: MemoryConfig()) + let disk = try! DiskStorage( + config: DiskConfig(name: "HybridDisk"), + transformer: Transformer.forCodable(ofType: User.self) + ) storage = HybridStorage(memoryStorage: memory, diskStorage: disk) } @@ -26,7 +29,7 @@ final class JSONDecoderExtensionsTests: XCTestCase { let user = try JSONDecoder.decode(json, to: User.self) try storage.setObject(user, forKey: "user") - let cachedObject = try storage.object(ofType: User.self, forKey: "user") + let cachedObject = try storage.object(forKey: "user") XCTAssertEqual(user, cachedObject) } @@ -36,7 +39,7 @@ final class JSONDecoderExtensionsTests: XCTestCase { let user = try JSONDecoder.decode(string, to: User.self) try storage.setObject(user, forKey: "user") - let cachedObject = try storage.object(ofType: User.self, forKey: "user") + let cachedObject = try storage.object(forKey: "user") XCTAssertEqual(cachedObject.firstName, "John") } } diff --git a/Tests/iOS/Tests/Storage/MemoryStorageTests.swift b/Tests/iOS/Tests/Storage/MemoryStorageTests.swift index 34fcc3b..60d8fd4 100644 --- a/Tests/iOS/Tests/Storage/MemoryStorageTests.swift +++ b/Tests/iOS/Tests/Storage/MemoryStorageTests.swift @@ -4,12 +4,12 @@ import XCTest final class MemoryStorageTests: XCTestCase { private let key = "youknownothing" private let testObject = User(firstName: "John", lastName: "Snow") - private var storage: MemoryStorage! + private var storage: MemoryStorage! private let config = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10) override func setUp() { super.setUp() - storage = MemoryStorage(config: config) + storage = MemoryStorage(config: config) } override func tearDown() { @@ -20,7 +20,7 @@ final class MemoryStorageTests: XCTestCase { /// Test that it saves an object func testSetObject() { storage.setObject(testObject, forKey: key) - let cachedObject = try! storage.object(ofType: User.self, forKey: key) + let cachedObject = try! storage.object(forKey: key) XCTAssertNotNil(cachedObject) XCTAssertEqual(cachedObject.firstName, testObject.firstName) XCTAssertEqual(cachedObject.lastName, testObject.lastName) @@ -28,12 +28,12 @@ final class MemoryStorageTests: XCTestCase { func testCacheEntry() { // Returns nil if entry doesn't exist - var entry = try? storage.entry(ofType: User.self, forKey: key) + var entry = try? storage.entry(forKey: key) XCTAssertNil(entry) // Returns entry if object exists storage.setObject(testObject, forKey: key) - entry = try! storage.entry(ofType: User.self, forKey: key) + entry = try! storage.entry(forKey: key) XCTAssertEqual(entry?.object.firstName, testObject.firstName) XCTAssertEqual(entry?.object.lastName, testObject.lastName) @@ -43,13 +43,13 @@ final class MemoryStorageTests: XCTestCase { func testSetObjectWithExpiry() { let date = Date().addingTimeInterval(1) storage.setObject(testObject, forKey: key, expiry: .seconds(1)) - var entry = try! storage.entry(ofType: User.self, forKey: key) + var entry = try! storage.entry(forKey: key) XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate, date.timeIntervalSinceReferenceDate, accuracy: 0.1) //Timer vs sleep: do not complicate sleep(1) - entry = try! storage.entry(ofType: User.self, forKey: key) + entry = try! storage.entry(forKey: key) XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate, date.timeIntervalSinceReferenceDate, accuracy: 0.1) @@ -59,7 +59,7 @@ final class MemoryStorageTests: XCTestCase { func testRemoveObject() { storage.setObject(testObject, forKey: key) storage.removeObject(forKey: key) - let cachedObject = try? storage.object(ofType: User.self, forKey: key) + let cachedObject = try? storage.object(forKey: key) XCTAssertNil(cachedObject) } @@ -68,7 +68,7 @@ final class MemoryStorageTests: XCTestCase { let expiry: Expiry = .date(Date().addingTimeInterval(-10)) storage.setObject(testObject, forKey: key, expiry: expiry) storage.removeObjectIfExpired(forKey: key) - let cachedObject = try? storage.object(ofType: User.self, forKey: key) + let cachedObject = try? storage.object(forKey: key) XCTAssertNil(cachedObject) } @@ -77,7 +77,7 @@ final class MemoryStorageTests: XCTestCase { func testRemoveObjectIfExpiredWhenNotExpired() { storage.setObject(testObject, forKey: key) storage.removeObjectIfExpired(forKey: key) - let cachedObject = try! storage.object(ofType: User.self, forKey: key) + let cachedObject = try! storage.object(forKey: key) XCTAssertNotNil(cachedObject) } @@ -85,16 +85,16 @@ final class MemoryStorageTests: XCTestCase { /// Test expired object func testExpiredObject() throws { storage.setObject(testObject, forKey: key, expiry: .seconds(0.9)) - XCTAssertFalse(try! storage.isExpiredObject(ofType: User.self, forKey: key)) + XCTAssertFalse(try! storage.isExpiredObject(forKey: key)) sleep(1) - XCTAssertTrue(try! storage.isExpiredObject(ofType: User.self, forKey: key)) + XCTAssertTrue(try! storage.isExpiredObject(forKey: key)) } /// Test that it clears cache directory func testRemoveAll() { storage.setObject(testObject, forKey: key) storage.removeAll() - let cachedObject = try? storage.object(ofType: User.self, forKey: key) + let cachedObject = try? storage.object(forKey: key) XCTAssertNil(cachedObject) } @@ -107,8 +107,8 @@ final class MemoryStorageTests: XCTestCase { storage.setObject(testObject, forKey: key1, expiry: expiry1) storage.setObject(testObject, forKey: key2, expiry: expiry2) storage.removeExpiredObjects() - let object1 = try? storage.object(ofType: User.self, forKey: key1) - let object2 = try! storage.object(ofType: User.self, forKey: key2) + let object1 = try? storage.object(forKey: key1) + let object2 = try! storage.object(forKey: key2) XCTAssertNil(object1) XCTAssertNotNil(object2) From fb823e9f1f42fa19e8d383d9d3b701deac6f93c0 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:31:33 +0200 Subject: [PATCH 17/31] Add TransformerFactory --- Cache.xcodeproj/project.pbxproj | 12 ++++++ Source/Shared/Library/Transformer.swift | 37 ------------------ .../Shared/Library/TransformerFactory.swift | 38 +++++++++++++++++++ Source/Shared/Storage/Storage+Support.swift | 6 +-- .../iOS/Tests/Storage/DiskStorageTests.swift | 4 +- 5 files changed, 55 insertions(+), 42 deletions(-) create mode 100644 Source/Shared/Library/TransformerFactory.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 945ecb7..92fe9ac 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -68,6 +68,11 @@ D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; }; D270149E20D1286F003B45C7 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; }; D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; }; + D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; }; + D27014A320D129A3003B45C7 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; }; + D27014A520D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; + D27014A620D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; + D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -163,6 +168,7 @@ D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; D270148F20D1251E003B45C7 /* TypeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapper.swift; sourceTree = ""; }; D270149320D125AC003B45C7 /* MemoryCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCapsule.swift; sourceTree = ""; }; + D27014A420D129EB003B45C7 /* TransformerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransformerFactory.swift; sourceTree = ""; }; D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = ""; }; D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = ""; }; D292DAFC1F6A970B0060F614 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; @@ -316,6 +322,7 @@ D270148B20D113EA003B45C7 /* Optional+Extension.swift */, D270148F20D1251E003B45C7 /* TypeWrapper.swift */, D270149320D125AC003B45C7 /* MemoryCapsule.swift */, + D27014A420D129EB003B45C7 /* TransformerFactory.swift */, ); path = Library; sourceTree = ""; @@ -789,6 +796,7 @@ D270147220D1018A003B45C7 /* Transformer.swift in Sources */, D21B668C1F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, + D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */, D270147E20D107DA003B45C7 /* SyncStorage.swift in Sources */, D292DAFF1F6A970B0060F614 /* Result.swift in Sources */, D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */, @@ -812,6 +820,7 @@ files = ( D2CF98201F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, D2CF98231F69427C00CE8F68 /* TestHelper.swift in Sources */, + D27014A320D129A3003B45C7 /* DiskStorageTests.swift in Sources */, D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */, D2CF98261F69427C00CE8F68 /* User.swift in Sources */, D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */, @@ -833,6 +842,7 @@ D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */, D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */, D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */, + D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */, D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */, D2CF98241F69427C00CE8F68 /* User.swift in Sources */, D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, @@ -857,6 +867,7 @@ D270147120D1018A003B45C7 /* Transformer.swift in Sources */, D21B66831F6A723C00125DE1 /* Types.swift in Sources */, D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, + D27014A620D129EB003B45C7 /* TransformerFactory.swift in Sources */, D270147D20D107DA003B45C7 /* SyncStorage.swift in Sources */, D292DAFE1F6A970B0060F614 /* Result.swift in Sources */, D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */, @@ -906,6 +917,7 @@ D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */, D270147020D1018A003B45C7 /* Transformer.swift in Sources */, D292DAFD1F6A970B0060F614 /* Result.swift in Sources */, + D27014A520D129EB003B45C7 /* TransformerFactory.swift in Sources */, D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */, D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */, diff --git a/Source/Shared/Library/Transformer.swift b/Source/Shared/Library/Transformer.swift index e2ccbca..71dabad 100644 --- a/Source/Shared/Library/Transformer.swift +++ b/Source/Shared/Library/Transformer.swift @@ -9,40 +9,3 @@ public class Transformer { self.fromData = fromData } } - -extension Transformer { - static func forData() -> Transformer { - let toData: (Data) throws -> Data = { $0 } - - let fromData: (Data) throws -> Data = { $0 } - - return Transformer(toData: toData, fromData: fromData) - } - - static func forImage() -> Transformer { - let toData: (Image) throws -> Data = { image in - return try image.cache_toData().unwrapOrThrow(error: StorageError.transformerFail) - } - - let fromData: (Data) throws -> Image = { data in - return try Image(data: data).unwrapOrThrow(error: StorageError.transformerFail) - } - - return Transformer(toData: toData, fromData: fromData) - } - - static func forCodable(ofType: U.Type) -> Transformer { - let toData: (U) throws -> Data = { object in - let wrapper = TypeWrapper(object: object) - let encoder = JSONEncoder() - return try encoder.encode(wrapper) - } - - let fromData: (Data) throws -> U = { data in - let decoder = JSONDecoder() - return try decoder.decode(TypeWrapper.self, from: data).object - } - - return Transformer(toData: toData, fromData: fromData) - } -} diff --git a/Source/Shared/Library/TransformerFactory.swift b/Source/Shared/Library/TransformerFactory.swift new file mode 100644 index 0000000..d6b7b67 --- /dev/null +++ b/Source/Shared/Library/TransformerFactory.swift @@ -0,0 +1,38 @@ +import Foundation + +public class TransformerFactory { + public static func forData() -> Transformer { + let toData: (Data) throws -> Data = { $0 } + + let fromData: (Data) throws -> Data = { $0 } + + return Transformer(toData: toData, fromData: fromData) + } + + public static func forImage() -> Transformer { + let toData: (Image) throws -> Data = { image in + return try image.cache_toData().unwrapOrThrow(error: StorageError.transformerFail) + } + + let fromData: (Data) throws -> Image = { data in + return try Image(data: data).unwrapOrThrow(error: StorageError.transformerFail) + } + + return Transformer(toData: toData, fromData: fromData) + } + + public static func forCodable(ofType: U.Type) -> Transformer { + let toData: (U) throws -> Data = { object in + let wrapper = TypeWrapper(object: object) + let encoder = JSONEncoder() + return try encoder.encode(wrapper) + } + + let fromData: (Data) throws -> U = { data in + let decoder = JSONDecoder() + return try decoder.decode(TypeWrapper.self, from: data).object + } + + return Transformer(toData: toData, fromData: fromData) + } +} diff --git a/Source/Shared/Storage/Storage+Support.swift b/Source/Shared/Storage/Storage+Support.swift index 76468c2..172e195 100644 --- a/Source/Shared/Storage/Storage+Support.swift +++ b/Source/Shared/Storage/Storage+Support.swift @@ -2,17 +2,17 @@ import Foundation public extension Storage { func supportData() -> Storage { - let storage = support(transformer: Transformer.forData()) + let storage = support(transformer: TransformerFactory.forData()) return storage } func supportImage() -> Storage { - let storage = support(transformer: Transformer.forImage()) + let storage = support(transformer: TransformerFactory.forImage()) return storage } func supportCodable(ofType: U.Type) -> Storage { - let storage = support(transformer: Transformer.forCodable(ofType: U.self)) + let storage = support(transformer: TransformerFactory.forCodable(ofType: U.self)) return storage } } diff --git a/Tests/iOS/Tests/Storage/DiskStorageTests.swift b/Tests/iOS/Tests/Storage/DiskStorageTests.swift index e7d4253..64b48ed 100644 --- a/Tests/iOS/Tests/Storage/DiskStorageTests.swift +++ b/Tests/iOS/Tests/Storage/DiskStorageTests.swift @@ -5,12 +5,12 @@ final class DiskStorageTests: XCTestCase { private let key = "youknownothing" private let testObject = User(firstName: "John", lastName: "Snow") private let fileManager = FileManager() - private var storage: DiskStorage! + private var storage: DiskStorage! private let config = DiskConfig(name: "Floppy") override func setUp() { super.setUp() - storage = try! DiskStorage(config: config) + storage = try! DiskStorage(config: config, transformer: Transformer.forCodable(ofType: User.self)) } override func tearDown() { From efdf1c73d1cab512cdbbfc81961a3ba93d9c6c79 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:35:48 +0200 Subject: [PATCH 18/31] Rename to StorageAware --- Source/Shared/Storage/DiskStorage.swift | 22 +++++++++++----------- Source/Shared/Storage/HybridStorage.swift | 2 +- Source/Shared/Storage/MemoryStorage.swift | 2 +- Source/Shared/Storage/Storage.swift | 2 +- Source/Shared/Storage/StorageAware.swift | 4 ++-- Source/Shared/Storage/SyncStorage.swift | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Source/Shared/Storage/DiskStorage.swift b/Source/Shared/Storage/DiskStorage.swift index 8b14644..5ddd6a4 100644 --- a/Source/Shared/Storage/DiskStorage.swift +++ b/Source/Shared/Storage/DiskStorage.swift @@ -1,23 +1,23 @@ import Foundation /// Save objects to file on disk -final class DiskStorage: StorageAware2 { +final public class DiskStorage { enum Error: Swift.Error { case fileEnumeratorFailed } /// File manager to read/write to the disk - private let fileManager: FileManager + public let fileManager: FileManager /// Configuration fileprivate let config: DiskConfig /// The computed path `directory+name` - private let path: String + public let path: String private let transformer: Transformer // MARK: - Initialization - required init(config: DiskConfig, fileManager: FileManager = FileManager.default, transformer: Transformer) throws { + public required init(config: DiskConfig, fileManager: FileManager = FileManager.default, transformer: Transformer) throws { self.config = config self.fileManager = fileManager self.transformer = transformer @@ -47,8 +47,8 @@ final class DiskStorage: StorageAware2 { } } -extension DiskStorage { - func entry(forKey key: String) throws -> Entry { +extension DiskStorage: StorageAware { + public func entry(forKey key: String) throws -> Entry { let filePath = makeFilePath(for: key) let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) let attributes = try fileManager.attributesOfItem(atPath: filePath) @@ -69,7 +69,7 @@ extension DiskStorage { ) } - func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { + public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { let expiry = expiry ?? config.expiry let data = try transformer.toData(object) let filePath = makeFilePath(for: key) @@ -77,16 +77,16 @@ extension DiskStorage { try fileManager.setAttributes([.modificationDate: expiry.date], ofItemAtPath: filePath) } - func removeObject(forKey key: String) throws { + public func removeObject(forKey key: String) throws { try fileManager.removeItem(atPath: makeFilePath(for: key)) } - func removeAll() throws { + public func removeAll() throws { try fileManager.removeItem(atPath: path) try createDirectory() } - func removeExpiredObjects() throws { + public func removeExpiredObjects() throws { let storageURL = URL(fileURLWithPath: path) let resourceKeys: [URLResourceKey] = [ .isDirectoryKey, @@ -234,7 +234,7 @@ extension DiskStorage { } } -extension DiskStorage { +public extension DiskStorage { func support(transformer: Transformer) -> DiskStorage { // swiftlint:disable force_try let storage = try! DiskStorage( diff --git a/Source/Shared/Storage/HybridStorage.swift b/Source/Shared/Storage/HybridStorage.swift index 0994329..240961c 100644 --- a/Source/Shared/Storage/HybridStorage.swift +++ b/Source/Shared/Storage/HybridStorage.swift @@ -11,7 +11,7 @@ public class HybridStorage { } } -extension HybridStorage: StorageAware2 { +extension HybridStorage: StorageAware { public func entry(forKey key: String) throws -> Entry { do { return try memoryStorage.entry(forKey: key) diff --git a/Source/Shared/Storage/MemoryStorage.swift b/Source/Shared/Storage/MemoryStorage.swift index 03b75e7..2153fe3 100644 --- a/Source/Shared/Storage/MemoryStorage.swift +++ b/Source/Shared/Storage/MemoryStorage.swift @@ -1,6 +1,6 @@ import Foundation -public class MemoryStorage: StorageAware2 { +public class MemoryStorage: StorageAware { fileprivate let cache = NSCache() // Memory cache keys fileprivate var keys = Set() diff --git a/Source/Shared/Storage/Storage.swift b/Source/Shared/Storage/Storage.swift index d68b1bc..1eccc86 100644 --- a/Source/Shared/Storage/Storage.swift +++ b/Source/Shared/Storage/Storage.swift @@ -45,7 +45,7 @@ public class Storage { public lazy var async = self.asyncStorage } -extension Storage: StorageAware2 { +extension Storage: StorageAware { public func entry(forKey key: String) throws -> Entry { return try self.syncStorage.entry(forKey: key) } diff --git a/Source/Shared/Storage/StorageAware.swift b/Source/Shared/Storage/StorageAware.swift index 4659de6..203d699 100644 --- a/Source/Shared/Storage/StorageAware.swift +++ b/Source/Shared/Storage/StorageAware.swift @@ -1,7 +1,7 @@ import Foundation /// A protocol used for saving and loading from storage -public protocol StorageAware2 { +public protocol StorageAware { associatedtype T /** Tries to retrieve the object from the storage. @@ -54,7 +54,7 @@ public protocol StorageAware2 { func isExpiredObject(forKey key: String) throws -> Bool } -public extension StorageAware2 { +public extension StorageAware { func object(forKey key: String) throws -> T { return try entry(forKey: key).object } diff --git a/Source/Shared/Storage/SyncStorage.swift b/Source/Shared/Storage/SyncStorage.swift index aeb158e..f5d93ce 100644 --- a/Source/Shared/Storage/SyncStorage.swift +++ b/Source/Shared/Storage/SyncStorage.swift @@ -13,7 +13,7 @@ public class SyncStorage { } } -extension SyncStorage: StorageAware2 { +extension SyncStorage: StorageAware { public func entry(forKey key: String) throws -> Entry { var entry: Entry! try serialQueue.sync { From 6a81af8c2bece39d30e3814e6a94dc9e8fd8c6ee Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:36:10 +0200 Subject: [PATCH 19/31] Use TransformerFactory --- Tests/iOS/Tests/Library/ObjectConverterTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/iOS/Tests/Library/ObjectConverterTests.swift b/Tests/iOS/Tests/Library/ObjectConverterTests.swift index 66b2a5e..7354ca8 100644 --- a/Tests/iOS/Tests/Library/ObjectConverterTests.swift +++ b/Tests/iOS/Tests/Library/ObjectConverterTests.swift @@ -9,7 +9,7 @@ final class JSONDecoderExtensionsTests: XCTestCase { let memory = MemoryStorage(config: MemoryConfig()) let disk = try! DiskStorage( config: DiskConfig(name: "HybridDisk"), - transformer: Transformer.forCodable(ofType: User.self) + transformer: TransformerFactory.forCodable(ofType: User.self) ) storage = HybridStorage(memoryStorage: memory, diskStorage: disk) From 00bded495d923cec684bc745ac272ff3d8fc9103 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:37:58 +0200 Subject: [PATCH 20/31] Update DiskStorageTests --- .../iOS/Tests/Storage/DiskStorageTests.swift | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Tests/iOS/Tests/Storage/DiskStorageTests.swift b/Tests/iOS/Tests/Storage/DiskStorageTests.swift index 64b48ed..6359149 100644 --- a/Tests/iOS/Tests/Storage/DiskStorageTests.swift +++ b/Tests/iOS/Tests/Storage/DiskStorageTests.swift @@ -10,7 +10,7 @@ final class DiskStorageTests: XCTestCase { override func setUp() { super.setUp() - storage = try! DiskStorage(config: config, transformer: Transformer.forCodable(ofType: User.self)) + storage = try! DiskStorage(config: config, transformer: TransformerFactory.forCodable(ofType: User.self)) } override func tearDown() { @@ -47,7 +47,7 @@ final class DiskStorageTests: XCTestCase { let customConfig = DiskConfig(name: "SSD", directory: url) - storage = try DiskStorage(config: customConfig) + storage = try DiskStorage(config: customConfig, transformer: TransformerFactory.forCodable(ofType: User.self)) XCTAssertEqual( storage.path, @@ -77,13 +77,13 @@ final class DiskStorageTests: XCTestCase { // Returns nil if entry doesn't exist var entry: Entry? do { - entry = try storage.entry(ofType: User.self, forKey: key) + entry = try storage.entry(forKey: key) } catch {} XCTAssertNil(entry) // Returns entry if object exists try storage.setObject(testObject, forKey: key) - entry = try storage.entry(ofType: User.self, forKey: key) + entry = try storage.entry(forKey: key) let attributes = try fileManager.attributesOfItem(atPath: storage.makeFilePath(for: key)) let expiry = Expiry.date(attributes[FileAttributeKey.modificationDate] as! Date) @@ -95,7 +95,7 @@ final class DiskStorageTests: XCTestCase { /// Test that it resolves cached object func testSetObject() throws { try storage.setObject(testObject, forKey: key) - let cachedObject: User? = try storage.object(ofType: User.self, forKey: key) + let cachedObject: User? = try storage.object(forKey: key) XCTAssertEqual(cachedObject?.firstName, testObject.firstName) XCTAssertEqual(cachedObject?.lastName, testObject.lastName) @@ -116,7 +116,7 @@ final class DiskStorageTests: XCTestCase { try storage.removeObjectIfExpired(forKey: key) var cachedObject: User? do { - cachedObject = try storage.object(ofType: User.self, forKey: key) + cachedObject = try storage.object(forKey: key) } catch {} XCTAssertNil(cachedObject) @@ -126,16 +126,16 @@ final class DiskStorageTests: XCTestCase { func testRemoveObjectIfExpiredWhenNotExpired() throws { try storage.setObject(testObject, forKey: key) try storage.removeObjectIfExpired(forKey: key) - let cachedObject: User? = try storage.object(ofType: User.self, forKey: key) + let cachedObject: User? = try storage.object(forKey: key) XCTAssertNotNil(cachedObject) } /// Test expired object func testExpiredObject() throws { try storage.setObject(testObject, forKey: key, expiry: .seconds(0.9)) - XCTAssertFalse(try! storage.isExpiredObject(ofType: User.self, forKey: key)) + XCTAssertFalse(try! storage.isExpiredObject(forKey: key)) sleep(1) - XCTAssertTrue(try! storage.isExpiredObject(ofType: User.self, forKey: key)) + XCTAssertTrue(try! storage.isExpiredObject(forKey: key)) } /// Test that it clears cache directory @@ -185,10 +185,10 @@ final class DiskStorageTests: XCTestCase { try storage.setObject(testObject, forKey: key2, expiry: expiry2) try storage.removeExpiredObjects() var object1: User? - let object2 = try storage.object(ofType: User.self, forKey: key2) + let object2 = try storage.object(forKey: key2) do { - object1 = try storage.object(ofType: User.self, forKey: key1) + object1 = try storage.object(forKey: key1) } catch {} XCTAssertNil(object1) From e69d029498240f2c029abc88c119a78bc5bc47e1 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:39:52 +0200 Subject: [PATCH 21/31] Update HybridStorageTests --- Cache.xcodeproj/project.pbxproj | 4 ++ .../Tests/Storage/HybridStorageTests.swift | 40 +++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 92fe9ac..7953e77 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -73,6 +73,8 @@ D27014A520D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; D27014A620D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; + D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; + D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -824,6 +826,7 @@ D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */, D2CF98261F69427C00CE8F68 /* User.swift in Sources */, D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */, + D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -844,6 +847,7 @@ D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */, D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */, D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */, + D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */, D2CF98241F69427C00CE8F68 /* User.swift in Sources */, D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, ); diff --git a/Tests/iOS/Tests/Storage/HybridStorageTests.swift b/Tests/iOS/Tests/Storage/HybridStorageTests.swift index afa4eb2..f975c21 100644 --- a/Tests/iOS/Tests/Storage/HybridStorageTests.swift +++ b/Tests/iOS/Tests/Storage/HybridStorageTests.swift @@ -5,13 +5,13 @@ final class HybridStorageTests: XCTestCase { private let cacheName = "WeirdoCache" private let key = "alongweirdkey" private let testObject = User(firstName: "John", lastName: "Targaryen") - private var storage: HybridStorage! + private var storage: HybridStorage! private let fileManager = FileManager() override func setUp() { super.setUp() - let memory = MemoryStorage(config: MemoryConfig()) - let disk = try! DiskStorage(config: DiskConfig(name: "HybridDisk")) + let memory = MemoryStorage(config: MemoryConfig()) + let disk = try! DiskStorage(config: DiskConfig(name: "HybridDisk"), transformer: TransformerFactory.forCodable(ofType: User.self)) storage = HybridStorage(memoryStorage: memory, diskStorage: disk) } @@ -24,17 +24,17 @@ final class HybridStorageTests: XCTestCase { func testSetObject() throws { try when("set to storage") { try storage.setObject(testObject, forKey: key) - let cachedObject = try storage.object(ofType: User.self, forKey: key) + let cachedObject = try storage.object(forKey: key) XCTAssertEqual(cachedObject, testObject) } try then("it is set to memory too") { - let memoryObject = try storage.memoryStorage.object(ofType: User.self, forKey: key) + let memoryObject = try storage.memoryStorage.object(forKey: key) XCTAssertNotNil(memoryObject) } try then("it is set to disk too") { - let diskObject = try storage.diskStorage.object(ofType: User.self, forKey: key) + let diskObject = try storage.diskStorage.object(forKey: key) XCTAssertNotNil(diskObject) } } @@ -42,7 +42,7 @@ final class HybridStorageTests: XCTestCase { func testEntry() throws { let expiryDate = Date() try storage.setObject(testObject, forKey: key, expiry: .date(expiryDate)) - let entry = try storage.entry(ofType: User.self, forKey: key) + let entry = try storage.entry(forKey: key) XCTAssertEqual(entry.object, testObject) XCTAssertEqual(entry.expiry.date, expiryDate) @@ -52,12 +52,12 @@ final class HybridStorageTests: XCTestCase { func testObjectCopyToMemory() throws { try when("set to disk only") { try storage.diskStorage.setObject(testObject, forKey: key) - let cachedObject: User = try storage.object(ofType: User.self, forKey: key) + let cachedObject: User = try storage.object(forKey: key) XCTAssertEqual(cachedObject, testObject) } try then("there is no object in memory") { - let inMemoryCachedObject = try storage.memoryStorage.object(ofType: User.self, forKey: key) + let inMemoryCachedObject = try storage.memoryStorage.object(forKey: key) XCTAssertEqual(inMemoryCachedObject, testObject) } } @@ -66,7 +66,7 @@ final class HybridStorageTests: XCTestCase { let date = Date().addingTimeInterval(3) try when("set to disk only") { try storage.diskStorage.setObject(testObject, forKey: key, expiry: .seconds(3)) - let entry = try storage.entry(ofType: User.self, forKey: key) + let entry = try storage.entry(forKey: key) //accuracy for slow disk processes XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate, date.timeIntervalSinceReferenceDate, @@ -74,7 +74,7 @@ final class HybridStorageTests: XCTestCase { } try then("there is no object in memory") { - let entry = try storage.memoryStorage.entry(ofType: User.self, forKey: key) + let entry = try storage.memoryStorage.entry(forKey: key) //accuracy for slow disk processes XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate, date.timeIntervalSinceReferenceDate, @@ -86,22 +86,22 @@ final class HybridStorageTests: XCTestCase { func testRemoveObject() throws { try given("set to storage") { try storage.setObject(testObject, forKey: key) - XCTAssertNotNil(try storage.object(ofType: User.self, forKey: key)) + XCTAssertNotNil(try storage.object(forKey: key)) } try when("remove object from storage") { try storage.removeObject(forKey: key) - let cachedObject = try? storage.object(ofType: User.self, forKey: key) + let cachedObject = try? storage.object(forKey: key) XCTAssertNil(cachedObject) } then("there is no object in memory") { - let memoryObject = try? storage.memoryStorage.object(ofType: User.self, forKey: key) + let memoryObject = try? storage.memoryStorage.object(forKey: key) XCTAssertNil(memoryObject) } then("there is no object on disk") { - let diskObject = try? storage.diskStorage.object(ofType: User.self, forKey: key) + let diskObject = try? storage.diskStorage.object(forKey: key) XCTAssertNil(diskObject) } } @@ -111,16 +111,16 @@ final class HybridStorageTests: XCTestCase { try when("set and remove all") { try storage.setObject(testObject, forKey: key) try storage.removeAll() - XCTAssertNil(try? storage.object(ofType: User.self, forKey: key)) + XCTAssertNil(try? storage.object(forKey: key)) } then("there is no object in memory") { - let memoryObject = try? storage.memoryStorage.object(ofType: User.self, forKey: key) + let memoryObject = try? storage.memoryStorage.object(forKey: key) XCTAssertNil(memoryObject) } then("there is no object on disk") { - let diskObject = try? storage.diskStorage.object(ofType: User.self, forKey: key) + let diskObject = try? storage.diskStorage.object(forKey: key) XCTAssertNil(diskObject) } } @@ -152,8 +152,8 @@ final class HybridStorageTests: XCTestCase { } then("object with key2 survived") { - XCTAssertNil(try? storage.object(ofType: User.self, forKey: key1)) - XCTAssertNotNil(try? storage.object(ofType: User.self, forKey: key2)) + XCTAssertNil(try? storage.object(forKey: key1)) + XCTAssertNotNil(try? storage.object(forKey: key2)) } } } From e96da4609a7cf9671a4a8cbc6989c2dfa2883aef Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:40:56 +0200 Subject: [PATCH 22/31] Expose all Storage classes --- Source/Shared/Storage/AsyncStorage.swift | 4 ++-- Source/Shared/Storage/HybridStorage.swift | 6 +++--- Source/Shared/Storage/SyncStorage.swift | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Shared/Storage/AsyncStorage.swift b/Source/Shared/Storage/AsyncStorage.swift index f2ca945..2c572e4 100644 --- a/Source/Shared/Storage/AsyncStorage.swift +++ b/Source/Shared/Storage/AsyncStorage.swift @@ -4,10 +4,10 @@ import Dispatch /// Manipulate storage in a "all async" manner. /// The completion closure will be called when operation completes. public class AsyncStorage { - fileprivate let innerStorage: HybridStorage + public let innerStorage: HybridStorage public let serialQueue: DispatchQueue - init(storage: HybridStorage, serialQueue: DispatchQueue) { + public init(storage: HybridStorage, serialQueue: DispatchQueue) { self.innerStorage = storage self.serialQueue = serialQueue } diff --git a/Source/Shared/Storage/HybridStorage.swift b/Source/Shared/Storage/HybridStorage.swift index 240961c..e52d807 100644 --- a/Source/Shared/Storage/HybridStorage.swift +++ b/Source/Shared/Storage/HybridStorage.swift @@ -2,10 +2,10 @@ import Foundation /// Use both memory and disk storage. Try on memory first. public class HybridStorage { - let memoryStorage: MemoryStorage - let diskStorage: DiskStorage + public let memoryStorage: MemoryStorage + public let diskStorage: DiskStorage - init(memoryStorage: MemoryStorage, diskStorage: DiskStorage) { + public init(memoryStorage: MemoryStorage, diskStorage: DiskStorage) { self.memoryStorage = memoryStorage self.diskStorage = diskStorage } diff --git a/Source/Shared/Storage/SyncStorage.swift b/Source/Shared/Storage/SyncStorage.swift index f5d93ce..36f2aad 100644 --- a/Source/Shared/Storage/SyncStorage.swift +++ b/Source/Shared/Storage/SyncStorage.swift @@ -4,10 +4,10 @@ import Dispatch /// Manipulate storage in a "all sync" manner. /// Block the current queue until the operation completes. public class SyncStorage { - fileprivate let innerStorage: HybridStorage - fileprivate let serialQueue: DispatchQueue + public let innerStorage: HybridStorage + public let serialQueue: DispatchQueue - init(innerStorage: HybridStorage, serialQueue: DispatchQueue) { + public init(innerStorage: HybridStorage, serialQueue: DispatchQueue) { self.innerStorage = innerStorage self.serialQueue = serialQueue } From 02245ec60de196d8023295581928179d0dfb35cc Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:46:21 +0200 Subject: [PATCH 23/31] Update SyncStorageTests --- Cache.xcodeproj/project.pbxproj | 4 ++++ Source/Shared/Storage/Storage.swift | 2 +- Source/Shared/Storage/SyncStorage.swift | 6 +++--- .../iOS/Tests/Storage/SyncStorageTests.swift | 20 +++++++++++-------- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 7953e77..c1acef8 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -75,6 +75,8 @@ D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; }; D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; + D27014AC20D12CC1003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; + D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -826,6 +828,7 @@ D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */, D2CF98261F69427C00CE8F68 /* User.swift in Sources */, D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */, + D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */, D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -849,6 +852,7 @@ D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */, D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */, D2CF98241F69427C00CE8F68 /* User.swift in Sources */, + D27014AC20D12CC1003B45C7 /* SyncStorageTests.swift in Sources */, D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/Shared/Storage/Storage.swift b/Source/Shared/Storage/Storage.swift index 1eccc86..840e9f5 100644 --- a/Source/Shared/Storage/Storage.swift +++ b/Source/Shared/Storage/Storage.swift @@ -20,7 +20,7 @@ public class Storage { let hybridStorage = HybridStorage(memoryStorage: memory, diskStorage: disk) let syncStorage = SyncStorage( - innerStorage: hybridStorage, + storage: hybridStorage, serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue") ) diff --git a/Source/Shared/Storage/SyncStorage.swift b/Source/Shared/Storage/SyncStorage.swift index 36f2aad..2339e6a 100644 --- a/Source/Shared/Storage/SyncStorage.swift +++ b/Source/Shared/Storage/SyncStorage.swift @@ -7,8 +7,8 @@ public class SyncStorage { public let innerStorage: HybridStorage public let serialQueue: DispatchQueue - public init(innerStorage: HybridStorage, serialQueue: DispatchQueue) { - self.innerStorage = innerStorage + public init(storage: HybridStorage, serialQueue: DispatchQueue) { + self.innerStorage = storage self.serialQueue = serialQueue } } @@ -51,7 +51,7 @@ extension SyncStorage: StorageAware { public extension SyncStorage { func support(transformer: Transformer) -> SyncStorage { let storage = SyncStorage( - innerStorage: innerStorage.support(transformer: transformer), + storage: innerStorage.support(transformer: transformer), serialQueue: serialQueue ) diff --git a/Tests/iOS/Tests/Storage/SyncStorageTests.swift b/Tests/iOS/Tests/Storage/SyncStorageTests.swift index 2193237..df14ad9 100644 --- a/Tests/iOS/Tests/Storage/SyncStorageTests.swift +++ b/Tests/iOS/Tests/Storage/SyncStorageTests.swift @@ -3,14 +3,17 @@ import Dispatch @testable import Cache final class SyncStorageTests: XCTestCase { - private var storage: SyncStorage! + private var storage: SyncStorage! let user = User(firstName: "John", lastName: "Snow") override func setUp() { super.setUp() - let memory = MemoryStorage(config: MemoryConfig()) - let primitive = TypeWrapperStorage(storage: memory) - storage = SyncStorage(storage: primitive, serialQueue: DispatchQueue(label: "Sync")) + + let memory = MemoryStorage(config: MemoryConfig()) + let disk = try! DiskStorage(config: DiskConfig(name: "HybridDisk"), transformer: TransformerFactory.forCodable(ofType: User.self)) + + let hybridStorage = HybridStorage(memoryStorage: memory, diskStorage: disk) + storage = SyncStorage(storage: hybridStorage, serialQueue: DispatchQueue(label: "Sync")) } override func tearDown() { @@ -20,24 +23,25 @@ final class SyncStorageTests: XCTestCase { func testSetObject() throws { try storage.setObject(user, forKey: "user") - let cachedObject = try storage.object(ofType: User.self, forKey: "user") + let cachedObject = try storage.object(forKey: "user") XCTAssertEqual(cachedObject, user) } func testRemoveAll() throws { + let intStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) try given("add a lot of objects") { try Array(0..<100).forEach { - try storage.setObject($0, forKey: "key-\($0)") + try intStorage.setObject($0, forKey: "key-\($0)") } } try when("remove all") { - try storage.removeAll() + try intStorage.removeAll() } try then("all are removed") { - XCTAssertFalse(try storage.existsObject(ofType: Int.self, forKey: "key-99")) + XCTAssertFalse(try intStorage.existsObject(forKey: "key-99")) } } } From ac0cedb627c753eee9866851a594ec5f204ac6e4 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 12:47:40 +0200 Subject: [PATCH 24/31] Update AsyncStorageTests --- Cache.xcodeproj/project.pbxproj | 4 ++++ .../iOS/Tests/Storage/AsyncStorageTests.swift | 20 +++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index c1acef8..0539ebc 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -77,6 +77,8 @@ D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; }; D27014AC20D12CC1003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; + D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; }; + D27014AF20D12D84003B45C7 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -827,6 +829,7 @@ D27014A320D129A3003B45C7 /* DiskStorageTests.swift in Sources */, D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */, D2CF98261F69427C00CE8F68 /* User.swift in Sources */, + D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */, D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */, D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */, D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */, @@ -851,6 +854,7 @@ D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */, D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */, D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */, + D27014AF20D12D84003B45C7 /* AsyncStorageTests.swift in Sources */, D2CF98241F69427C00CE8F68 /* User.swift in Sources */, D27014AC20D12CC1003B45C7 /* SyncStorageTests.swift in Sources */, D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */, diff --git a/Tests/iOS/Tests/Storage/AsyncStorageTests.swift b/Tests/iOS/Tests/Storage/AsyncStorageTests.swift index bcb63f1..4cc353c 100644 --- a/Tests/iOS/Tests/Storage/AsyncStorageTests.swift +++ b/Tests/iOS/Tests/Storage/AsyncStorageTests.swift @@ -3,16 +3,15 @@ import Dispatch @testable import Cache final class AsyncStorageTests: XCTestCase { - private var storage: AsyncStorage! + private var storage: AsyncStorage! let user = User(firstName: "John", lastName: "Snow") override func setUp() { super.setUp() - let memory = MemoryStorage(config: MemoryConfig()) - let disk = try! DiskStorage(config: DiskConfig(name: "Async Disk")) - let hybrid = HybridStorage(memoryStorage: memory, diskStorage: disk) - let primitive = TypeWrapperStorage(storage: hybrid) - storage = AsyncStorage(storage: primitive, serialQueue: DispatchQueue(label: "Async")) + let memory = MemoryStorage(config: MemoryConfig()) + let disk = try! DiskStorage(config: DiskConfig(name: "Async Disk"), transformer: TransformerFactory.forCodable(ofType: User.self)) + let hybrid = HybridStorage(memoryStorage: memory, diskStorage: disk) + storage = AsyncStorage(storage: hybrid, serialQueue: DispatchQueue(label: "Async")) } override func tearDown() { @@ -24,7 +23,7 @@ final class AsyncStorageTests: XCTestCase { let expectation = self.expectation(description: #function) storage.setObject(user, forKey: "user", completion: { _ in }) - storage.object(ofType: User.self, forKey: "user", completion: { result in + storage.object(forKey: "user", completion: { result in switch result { case .value(let cachedUser): XCTAssertEqual(cachedUser, self.user) @@ -38,19 +37,20 @@ final class AsyncStorageTests: XCTestCase { } func testRemoveAll() { + let intStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) let expectation = self.expectation(description: #function) given("add a lot of objects") { Array(0..<100).forEach { - storage.setObject($0, forKey: "key-\($0)", completion: { _ in }) + intStorage.setObject($0, forKey: "key-\($0)", completion: { _ in }) } } when("remove all") { - storage.removeAll(completion: { _ in }) + intStorage.removeAll(completion: { _ in }) } then("all are removed") { - storage.existsObject(ofType: Int.self, forKey: "key-99", completion: { result in + intStorage.existsObject(forKey: "key-99", completion: { result in switch result { case .value: XCTFail() From 0aff00c087039282ea7c9cef20c24e0b085aab96 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:05:56 +0200 Subject: [PATCH 25/31] Add tests for Data, Date, URL --- Cache.xcodeproj/project.pbxproj | 8 +- .../Tests/Storage/StorageSupportTests.swift | 191 ++++++++++++++++++ .../Storage/TypeWrapperStorageTests.swift | 154 -------------- 3 files changed, 197 insertions(+), 156 deletions(-) create mode 100644 Tests/iOS/Tests/Storage/StorageSupportTests.swift delete mode 100644 Tests/iOS/Tests/Storage/TypeWrapperStorageTests.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index 0539ebc..e844693 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -79,6 +79,8 @@ D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; }; D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; }; D27014AF20D12D84003B45C7 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; }; + D27014B020D12E37003B45C7 /* StorageSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */; }; + D27014B120D12E38003B45C7 /* StorageSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */; }; D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; }; @@ -200,7 +202,7 @@ D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HybridStorageTests.swift; sourceTree = ""; }; D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryStorageTests.swift; sourceTree = ""; }; D2CF98861F695B8F00CE8F68 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; - D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapperStorageTests.swift; sourceTree = ""; }; + D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageSupportTests.swift; sourceTree = ""; }; D2D4CC151FA3145000E4A2D5 /* MD5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = ""; }; D2D4CC191FA3166900E4A2D5 /* MD5Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5Tests.swift; sourceTree = ""; }; D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryWrapper.swift; sourceTree = ""; }; @@ -386,7 +388,7 @@ D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */, D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */, D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */, - D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */, + D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */, D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */, D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */, D236F3191F6BEF73004EE01F /* StorageTests.swift */, @@ -831,6 +833,7 @@ D2CF98261F69427C00CE8F68 /* User.swift in Sources */, D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */, D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */, + D27014B120D12E38003B45C7 /* StorageSupportTests.swift in Sources */, D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */, D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */, ); @@ -852,6 +855,7 @@ D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */, D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */, D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */, + D27014B020D12E37003B45C7 /* StorageSupportTests.swift in Sources */, D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */, D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */, D27014AF20D12D84003B45C7 /* AsyncStorageTests.swift in Sources */, diff --git a/Tests/iOS/Tests/Storage/StorageSupportTests.swift b/Tests/iOS/Tests/Storage/StorageSupportTests.swift new file mode 100644 index 0000000..e90de1f --- /dev/null +++ b/Tests/iOS/Tests/Storage/StorageSupportTests.swift @@ -0,0 +1,191 @@ +import XCTest +@testable import Cache + +final class StorageSupportTests: XCTestCase { + private var storage: HybridStorage! + + override func setUp() { + super.setUp() + let memory = MemoryStorage(config: MemoryConfig()) + let disk = try! DiskStorage(config: DiskConfig(name: "PrimitiveDisk"), transformer: TransformerFactory.forCodable(ofType: Bool.self)) + storage = HybridStorage(memoryStorage: memory, diskStorage: disk) + } + + override func tearDown() { + try? storage.removeAll() + super.tearDown() + } + + func testSetPrimitive() throws { + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Bool.self)) + try s.setObject(true, forKey: "bool") + XCTAssertEqual(try s.object(forKey: "bool"), true) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Bool].self)) + try s.setObject([true, false, true], forKey: "array of bools") + XCTAssertEqual(try s.object(forKey: "array of bools"), [true, false, true]) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + try s.setObject("one", forKey: "string") + XCTAssertEqual(try s.object(forKey: "string"), "one") + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [String].self)) + try s.setObject(["one", "two", "three"], forKey: "array of strings") + XCTAssertEqual(try s.object(forKey: "array of strings"), ["one", "two", "three"]) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + try s.setObject(10, forKey: "int") + XCTAssertEqual(try s.object(forKey: "int"), 10) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Int].self)) + try s.setObject([1, 2, 3], forKey: "array of ints") + XCTAssertEqual(try s.object(forKey: "array of ints"), [1, 2, 3]) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Float.self)) + let float: Float = 1.1 + try s.setObject(float, forKey: "float") + XCTAssertEqual(try s.object(forKey: "float"), float) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Float].self)) + let floats: [Float] = [1.1, 1.2, 1.3] + try s.setObject(floats, forKey: "array of floats") + XCTAssertEqual(try s.object(forKey: "array of floats"), floats) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Double.self)) + let double: Double = 1.1 + try s.setObject(double, forKey: "double") + XCTAssertEqual(try s.object(forKey: "double"), double) + } + + do { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Double].self)) + let doubles: [Double] = [1.1, 1.2, 1.3] + try s.setObject(doubles, forKey: "array of doubles") + XCTAssertEqual(try s.object(forKey: "array of doubles"), doubles) + } + } + + func testSetData() { + let s = storage.support(transformer: TransformerFactory.forData()) + + do { + let string = "Hello" + let data = string.data(using: .utf8)! + try s.setObject(data, forKey: "data") + + let cachedObject = try s.object(forKey: "data") + let cachedString = String(data: cachedObject, encoding: .utf8) + + XCTAssertEqual(cachedString, string) + } catch { + XCTFail(error.localizedDescription) + } + } + + func testSetDate() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Date.self)) + + let date = Date(timeIntervalSince1970: 100) + try s.setObject(date, forKey: "date") + let cachedObject = try s.object(forKey: "date") + + XCTAssertEqual(date, cachedObject) + } + + func testSetURL() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: URL.self)) + let url = URL(string: "https://hyper.no")! + try s.setObject(url, forKey: "url") + let cachedObject = try s.object(forKey: "url") + + XCTAssertEqual(url, cachedObject) + } + + func testWithSet() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Set.self)) + let set = Set(arrayLiteral: 1, 2, 3) + try s.setObject(set, forKey: "set") + XCTAssertEqual(try s.object(forKey: "set") as Set, set) + } + + /* + func testWithSimpleDictionary() throws { + let dict: [String: Int] = [ + "key1": 1, + "key2": 2 + ] + + try storage.setObject(dict, forKey: "dict") + let cachedObject = try storage.object(ofType: [String: Int].self, forKey: "dict") as [String: Int] + XCTAssertEqual(cachedObject, dict) + } + + func testWithComplexDictionary() { + let _: [String: Any] = [ + "key1": 1, + "key2": 2 + ] + + // fatal error: Dictionary does not conform to Encodable because Any does not conform to Encodable + // try storage.setObject(dict, forKey: "dict") + } + + func testSameKey() throws { + let user = User(firstName: "John", lastName: "Snow") + let key = "keyMadeOfDragonGlass" + try storage.setObject(user, forKey: key) + try storage.setObject("Dragonstone", forKey: key) + + XCTAssertNil(try? storage.object(ofType: User.self, forKey: key)) + XCTAssertNotNil(try storage.object(ofType: String.self, forKey: key)) + } + + func testIntFloat() throws { + let key = "key" + try storage.setObject(10, forKey: key) + + try then("Casting to int or float is the same") { + XCTAssertEqual(try storage.object(ofType: Int.self, forKey: key), 10) + XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10) + } + } + + func testFloatDouble() throws { + let key = "key" + try storage.setObject(10.5, forKey: key) + + try then("Casting to float or double is the same") { + XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10.5) + XCTAssertEqual(try storage.object(ofType: Double.self, forKey: key), 10.5) + } + } + + func testCastingToAnotherType() throws { + try storage.setObject("Hello", forKey: "string") + + do { + let cachedObject = try storage.object(ofType: Int.self, forKey: "string") + XCTAssertEqual(cachedObject, 10) + } catch { + XCTAssertTrue(error is DecodingError) + } + } + */ +} diff --git a/Tests/iOS/Tests/Storage/TypeWrapperStorageTests.swift b/Tests/iOS/Tests/Storage/TypeWrapperStorageTests.swift deleted file mode 100644 index 5713ac3..0000000 --- a/Tests/iOS/Tests/Storage/TypeWrapperStorageTests.swift +++ /dev/null @@ -1,154 +0,0 @@ -import XCTest -@testable import Cache - -final class TypeWrapperStorageTests: XCTestCase { - private var storage: TypeWrapperStorage! - - override func setUp() { - super.setUp() - let memory = MemoryStorage(config: MemoryConfig()) - let disk = try! DiskStorage(config: DiskConfig(name: "PrimitiveDisk")) - let hybrid = HybridStorage(memoryStorage: memory, diskStorage: disk) - storage = TypeWrapperStorage(storage: hybrid) - } - - override func tearDown() { - try? storage.removeAll() - super.tearDown() - } - - func testSetPrimitive() throws { - try storage.setObject(true, forKey: "bool") - XCTAssertEqual(try storage.object(ofType: Bool.self, forKey: "bool"), true) - - try storage.setObject([true, false, true], forKey: "array of bools") - XCTAssertEqual(try storage.object(ofType: [Bool].self, forKey: "array of bools"), [true, false, true]) - - try storage.setObject("one", forKey: "string") - XCTAssertEqual(try storage.object(ofType: String.self, forKey: "string"), "one") - - try storage.setObject(["one", "two", "three"], forKey: "array of strings") - XCTAssertEqual(try storage.object(ofType: [String].self, forKey: "array of strings"), ["one", "two", "three"]) - - try storage.setObject(10, forKey: "int") - XCTAssertEqual(try storage.object(ofType: Int.self, forKey: "int"), 10) - - try storage.setObject([1, 2, 3], forKey: "array of ints") - XCTAssertEqual(try storage.object(ofType: [Int].self, forKey: "array of ints"), [1, 2, 3]) - - let float: Float = 1.1 - try storage.setObject(float, forKey: "float") - XCTAssertEqual(try storage.object(ofType: Float.self, forKey: "float"), float) - - let floats: [Float] = [1.1, 1.2, 1.3] - try storage.setObject(floats, forKey: "array of floats") - XCTAssertEqual(try storage.object(ofType: [Float].self, forKey: "array of floats"), floats) - - let double: Double = 1.1 - try storage.setObject(double, forKey: "double") - XCTAssertEqual(try storage.object(ofType: Double.self, forKey: "double"), double) - - let doubles: [Double] = [1.1, 1.2, 1.3] - try storage.setObject(doubles, forKey: "array of doubles") - XCTAssertEqual(try storage.object(ofType: [Double].self, forKey: "array of doubles"), doubles) - } - - func testSetData() { - do { - let string = "Hello" - let data = string.data(using: .utf8) - try storage.setObject(data, forKey: "data") - - let cachedObject = try storage.object(ofType: Data.self, forKey: "data") - let cachedString = String(data: cachedObject, encoding: .utf8) - - XCTAssertEqual(cachedString, string) - } catch { - XCTFail(error.localizedDescription) - } - } - - func testSetDate() throws { - let date = Date(timeIntervalSince1970: 100) - try storage.setObject(date, forKey: "date") - let cachedObject = try storage.object(ofType: Date.self, forKey: "date") - - XCTAssertEqual(date, cachedObject) - } - - func testSetURL() throws { - let url = URL(string: "https://hyper.no") - try storage.setObject(url, forKey: "url") - let cachedObject = try storage.object(ofType: URL.self, forKey: "url") - - XCTAssertEqual(url, cachedObject) - } - - func testWithSet() throws { - let set = Set(arrayLiteral: 1, 2, 3) - try storage.setObject(set, forKey: "set") - XCTAssertEqual(try storage.object(ofType: Set.self, forKey: "set") as Set, set) - } - - func testWithSimpleDictionary() throws { - let dict: [String: Int] = [ - "key1": 1, - "key2": 2 - ] - - try storage.setObject(dict, forKey: "dict") - let cachedObject = try storage.object(ofType: [String: Int].self, forKey: "dict") as [String: Int] - XCTAssertEqual(cachedObject, dict) - } - - func testWithComplexDictionary() { - let _: [String: Any] = [ - "key1": 1, - "key2": 2 - ] - - // fatal error: Dictionary does not conform to Encodable because Any does not conform to Encodable - // try storage.setObject(dict, forKey: "dict") - } - - func testSameKey() throws { - let user = User(firstName: "John", lastName: "Snow") - let key = "keyMadeOfDragonGlass" - try storage.setObject(user, forKey: key) - try storage.setObject("Dragonstone", forKey: key) - - XCTAssertNil(try? storage.object(ofType: User.self, forKey: key)) - XCTAssertNotNil(try storage.object(ofType: String.self, forKey: key)) - } - - func testIntFloat() throws { - let key = "key" - try storage.setObject(10, forKey: key) - - try then("Casting to int or float is the same") { - XCTAssertEqual(try storage.object(ofType: Int.self, forKey: key), 10) - XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10) - } - } - - func testFloatDouble() throws { - let key = "key" - try storage.setObject(10.5, forKey: key) - - try then("Casting to float or double is the same") { - XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10.5) - XCTAssertEqual(try storage.object(ofType: Double.self, forKey: key), 10.5) - } - } - - func testCastingToAnotherType() throws { - try storage.setObject("Hello", forKey: "string") - - do { - let cachedObject = try storage.object(ofType: Int.self, forKey: "string") - XCTAssertEqual(cachedObject, 10) - } catch { - XCTAssertTrue(error is DecodingError) - } - } -} From 3390ee24c9478066fa1eabecc34a771cee6bcc8a Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:10:51 +0200 Subject: [PATCH 26/31] Add test for overriden --- .../Tests/Storage/StorageSupportTests.swift | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/Tests/iOS/Tests/Storage/StorageSupportTests.swift b/Tests/iOS/Tests/Storage/StorageSupportTests.swift index e90de1f..e58698a 100644 --- a/Tests/iOS/Tests/Storage/StorageSupportTests.swift +++ b/Tests/iOS/Tests/Storage/StorageSupportTests.swift @@ -125,15 +125,16 @@ final class StorageSupportTests: XCTestCase { XCTAssertEqual(try s.object(forKey: "set") as Set, set) } - /* func testWithSimpleDictionary() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [String: Int].self)) + let dict: [String: Int] = [ "key1": 1, "key2": 2 ] - try storage.setObject(dict, forKey: "dict") - let cachedObject = try storage.object(ofType: [String: Int].self, forKey: "dict") as [String: Int] + try s.setObject(dict, forKey: "dict") + let cachedObject = try s.object(forKey: "dict") as [String: Int] XCTAssertEqual(cachedObject, dict) } @@ -148,44 +149,57 @@ final class StorageSupportTests: XCTestCase { } func testSameKey() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: User.self)) + let user = User(firstName: "John", lastName: "Snow") let key = "keyMadeOfDragonGlass" - try storage.setObject(user, forKey: key) - try storage.setObject("Dragonstone", forKey: key) + try s.setObject(user, forKey: key) - XCTAssertNil(try? storage.object(ofType: User.self, forKey: key)) - XCTAssertNotNil(try storage.object(ofType: String.self, forKey: key)) + do { + let stringStorage = s.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + try stringStorage.setObject("Dragonstone", forKey: key) + } + + XCTAssertNil(try? s.object(forKey: key)) + XCTAssertNotNil(try s.object(forKey: key)) } func testIntFloat() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Float.self)) let key = "key" - try storage.setObject(10, forKey: key) + try s.setObject(10, forKey: key) try then("Casting to int or float is the same") { - XCTAssertEqual(try storage.object(ofType: Int.self, forKey: key), 10) - XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10) + XCTAssertEqual(try s.object(forKey: key), 10) + + let intStorage = s.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + XCTAssertEqual(try intStorage.object(forKey: key), 10) } } func testFloatDouble() throws { + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Float.self)) let key = "key" - try storage.setObject(10.5, forKey: key) + try s.setObject(10.5, forKey: key) try then("Casting to float or double is the same") { - XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10.5) - XCTAssertEqual(try storage.object(ofType: Double.self, forKey: key), 10.5) + XCTAssertEqual(try s.object(forKey: key), 10.5) + + let doubleStorage = s.support(transformer: TransformerFactory.forCodable(ofType: Double.self)) + XCTAssertEqual(try doubleStorage.object(forKey: key), 10.5) } } func testCastingToAnotherType() throws { - try storage.setObject("Hello", forKey: "string") + let s = storage.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + try s.setObject("Hello", forKey: "string") do { - let cachedObject = try storage.object(ofType: Int.self, forKey: "string") - XCTAssertEqual(cachedObject, 10) + let intStorage = s.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let _ = try intStorage.object(forKey: "string") + XCTFail() } catch { XCTAssertTrue(error is DecodingError) } } - */ } From 385371eb13936fde7040ea9e2ea577448080d623 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:16:03 +0200 Subject: [PATCH 27/31] Add testOverriden --- .../iOS/Tests/Storage/StorageSupportTests.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Tests/iOS/Tests/Storage/StorageSupportTests.swift b/Tests/iOS/Tests/Storage/StorageSupportTests.swift index e58698a..3f36cc9 100644 --- a/Tests/iOS/Tests/Storage/StorageSupportTests.swift +++ b/Tests/iOS/Tests/Storage/StorageSupportTests.swift @@ -202,4 +202,20 @@ final class StorageSupportTests: XCTestCase { XCTAssertTrue(error is DecodingError) } } + + func testOverriden() throws { + let intStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let stringStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + + let key = "sameKey" + + try intStorage.setObject(1, forKey: key) + try stringStorage.setObject("hello world", forKey: key) + + let intValue = try? intStorage.object(forKey: key) + let stringValue = try? stringStorage.object(forKey: key) + + XCTAssertNil(intValue) + XCTAssertNotNil(stringValue) + } } From 40a8b3007d2570d03ade1256f8bca6fdf4609134 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:17:13 +0200 Subject: [PATCH 28/31] Rename from support to transform --- Cache.xcodeproj/project.pbxproj | 16 ++--- Source/Shared/Storage/AsyncStorage.swift | 4 +- Source/Shared/Storage/DiskStorage.swift | 2 +- Source/Shared/Storage/HybridStorage.swift | 6 +- Source/Shared/Storage/MemoryStorage.swift | 2 +- Source/Shared/Storage/Storage+Support.swift | 18 ------ Source/Shared/Storage/Storage+Transform.swift | 18 ++++++ Source/Shared/Storage/Storage.swift | 6 +- Source/Shared/Storage/SyncStorage.swift | 4 +- .../iOS/Tests/Storage/AsyncStorageTests.swift | 2 +- .../Tests/Storage/StorageSupportTests.swift | 62 +++++++------------ .../iOS/Tests/Storage/SyncStorageTests.swift | 2 +- 12 files changed, 63 insertions(+), 79 deletions(-) delete mode 100644 Source/Shared/Storage/Storage+Support.swift create mode 100644 Source/Shared/Storage/Storage+Transform.swift diff --git a/Cache.xcodeproj/project.pbxproj b/Cache.xcodeproj/project.pbxproj index e844693..1403ff6 100644 --- a/Cache.xcodeproj/project.pbxproj +++ b/Cache.xcodeproj/project.pbxproj @@ -54,9 +54,9 @@ D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; }; D270148520D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; }; D270148620D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; }; - D270148820D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; - D270148920D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; - D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Support.swift */; }; + D270148820D11040003B45C7 /* Storage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Transform.swift */; }; + D270148920D11040003B45C7 /* Storage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Transform.swift */; }; + D270148A20D11040003B45C7 /* Storage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Transform.swift */; }; D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; }; @@ -172,7 +172,7 @@ D270147B20D107DA003B45C7 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = ""; }; D270147F20D10982003B45C7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; D270148320D10E76003B45C7 /* AsyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage.swift; sourceTree = ""; }; - D270148720D11040003B45C7 /* Storage+Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Support.swift"; sourceTree = ""; }; + D270148720D11040003B45C7 /* Storage+Transform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Transform.swift"; sourceTree = ""; }; D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; D270148F20D1251E003B45C7 /* TypeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapper.swift; sourceTree = ""; }; D270149320D125AC003B45C7 /* MemoryCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCapsule.swift; sourceTree = ""; }; @@ -345,7 +345,7 @@ D270147B20D107DA003B45C7 /* SyncStorage.swift */, D270147F20D10982003B45C7 /* Storage.swift */, D270148320D10E76003B45C7 /* AsyncStorage.swift */, - D270148720D11040003B45C7 /* Storage+Support.swift */, + D270148720D11040003B45C7 /* Storage+Transform.swift */, ); path = Storage; sourceTree = ""; @@ -810,7 +810,7 @@ D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */, D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */, - D270148A20D11040003B45C7 /* Storage+Support.swift in Sources */, + D270148A20D11040003B45C7 /* Storage+Transform.swift in Sources */, D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147A20D1046A003B45C7 /* HybridStorage.swift in Sources */, @@ -889,7 +889,7 @@ D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */, D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */, D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */, - D270148920D11040003B45C7 /* Storage+Support.swift in Sources */, + D270148920D11040003B45C7 /* Storage+Transform.swift in Sources */, D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */, D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */, D270147920D1046A003B45C7 /* HybridStorage.swift in Sources */, @@ -937,7 +937,7 @@ D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */, D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */, D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */, - D270148820D11040003B45C7 /* Storage+Support.swift in Sources */, + D270148820D11040003B45C7 /* Storage+Transform.swift in Sources */, D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */, D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */, D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */, diff --git a/Source/Shared/Storage/AsyncStorage.swift b/Source/Shared/Storage/AsyncStorage.swift index 2c572e4..dcd5e6b 100644 --- a/Source/Shared/Storage/AsyncStorage.swift +++ b/Source/Shared/Storage/AsyncStorage.swift @@ -118,9 +118,9 @@ extension AsyncStorage { } public extension AsyncStorage { - func support(transformer: Transformer) -> AsyncStorage { + func transform(transformer: Transformer) -> AsyncStorage { let storage = AsyncStorage( - storage: innerStorage.support(transformer: transformer), + storage: innerStorage.transform(transformer: transformer), serialQueue: serialQueue ) diff --git a/Source/Shared/Storage/DiskStorage.swift b/Source/Shared/Storage/DiskStorage.swift index 5ddd6a4..420c20e 100644 --- a/Source/Shared/Storage/DiskStorage.swift +++ b/Source/Shared/Storage/DiskStorage.swift @@ -235,7 +235,7 @@ extension DiskStorage { } public extension DiskStorage { - func support(transformer: Transformer) -> DiskStorage { + func transform(transformer: Transformer) -> DiskStorage { // swiftlint:disable force_try let storage = try! DiskStorage( config: config, diff --git a/Source/Shared/Storage/HybridStorage.swift b/Source/Shared/Storage/HybridStorage.swift index e52d807..db81770 100644 --- a/Source/Shared/Storage/HybridStorage.swift +++ b/Source/Shared/Storage/HybridStorage.swift @@ -45,10 +45,10 @@ extension HybridStorage: StorageAware { } public extension HybridStorage { - func support(transformer: Transformer) -> HybridStorage { + func transform(transformer: Transformer) -> HybridStorage { let storage = HybridStorage( - memoryStorage: memoryStorage.support(), - diskStorage: diskStorage.support(transformer: transformer) + memoryStorage: memoryStorage.transform(), + diskStorage: diskStorage.transform(transformer: transformer) ) return storage diff --git a/Source/Shared/Storage/MemoryStorage.swift b/Source/Shared/Storage/MemoryStorage.swift index 2153fe3..c98655e 100644 --- a/Source/Shared/Storage/MemoryStorage.swift +++ b/Source/Shared/Storage/MemoryStorage.swift @@ -58,7 +58,7 @@ extension MemoryStorage { } public extension MemoryStorage { - func support() -> MemoryStorage { + func transform() -> MemoryStorage { let storage = MemoryStorage(config: config) return storage } diff --git a/Source/Shared/Storage/Storage+Support.swift b/Source/Shared/Storage/Storage+Support.swift deleted file mode 100644 index 172e195..0000000 --- a/Source/Shared/Storage/Storage+Support.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -public extension Storage { - func supportData() -> Storage { - let storage = support(transformer: TransformerFactory.forData()) - return storage - } - - func supportImage() -> Storage { - let storage = support(transformer: TransformerFactory.forImage()) - return storage - } - - func supportCodable(ofType: U.Type) -> Storage { - let storage = support(transformer: TransformerFactory.forCodable(ofType: U.self)) - return storage - } -} diff --git a/Source/Shared/Storage/Storage+Transform.swift b/Source/Shared/Storage/Storage+Transform.swift new file mode 100644 index 0000000..d628fd8 --- /dev/null +++ b/Source/Shared/Storage/Storage+Transform.swift @@ -0,0 +1,18 @@ +import Foundation + +public extension Storage { + func transformData() -> Storage { + let storage = transform(transformer: TransformerFactory.forData()) + return storage + } + + func transformImage() -> Storage { + let storage = transform(transformer: TransformerFactory.forImage()) + return storage + } + + func transformCodable(ofType: U.Type) -> Storage { + let storage = transform(transformer: TransformerFactory.forCodable(ofType: U.self)) + return storage + } +} diff --git a/Source/Shared/Storage/Storage.swift b/Source/Shared/Storage/Storage.swift index 840e9f5..2f74492 100644 --- a/Source/Shared/Storage/Storage.swift +++ b/Source/Shared/Storage/Storage.swift @@ -68,10 +68,10 @@ extension Storage: StorageAware { } public extension Storage { - func support(transformer: Transformer) -> Storage { + func transform(transformer: Transformer) -> Storage { let storage = Storage( - syncStorage: syncStorage.support(transformer: transformer), - asyncStorage: asyncStorage.support(transformer: transformer) + syncStorage: syncStorage.transform(transformer: transformer), + asyncStorage: asyncStorage.transform(transformer: transformer) ) return storage } diff --git a/Source/Shared/Storage/SyncStorage.swift b/Source/Shared/Storage/SyncStorage.swift index 2339e6a..4b4282a 100644 --- a/Source/Shared/Storage/SyncStorage.swift +++ b/Source/Shared/Storage/SyncStorage.swift @@ -49,9 +49,9 @@ extension SyncStorage: StorageAware { } public extension SyncStorage { - func support(transformer: Transformer) -> SyncStorage { + func transform(transformer: Transformer) -> SyncStorage { let storage = SyncStorage( - storage: innerStorage.support(transformer: transformer), + storage: innerStorage.transform(transformer: transformer), serialQueue: serialQueue ) diff --git a/Tests/iOS/Tests/Storage/AsyncStorageTests.swift b/Tests/iOS/Tests/Storage/AsyncStorageTests.swift index 4cc353c..b2a181e 100644 --- a/Tests/iOS/Tests/Storage/AsyncStorageTests.swift +++ b/Tests/iOS/Tests/Storage/AsyncStorageTests.swift @@ -37,7 +37,7 @@ final class AsyncStorageTests: XCTestCase { } func testRemoveAll() { - let intStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) let expectation = self.expectation(description: #function) given("add a lot of objects") { Array(0..<100).forEach { diff --git a/Tests/iOS/Tests/Storage/StorageSupportTests.swift b/Tests/iOS/Tests/Storage/StorageSupportTests.swift index 3f36cc9..41fa4b2 100644 --- a/Tests/iOS/Tests/Storage/StorageSupportTests.swift +++ b/Tests/iOS/Tests/Storage/StorageSupportTests.swift @@ -18,64 +18,64 @@ final class StorageSupportTests: XCTestCase { func testSetPrimitive() throws { do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Bool.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Bool.self)) try s.setObject(true, forKey: "bool") XCTAssertEqual(try s.object(forKey: "bool"), true) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Bool].self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Bool].self)) try s.setObject([true, false, true], forKey: "array of bools") XCTAssertEqual(try s.object(forKey: "array of bools"), [true, false, true]) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self)) try s.setObject("one", forKey: "string") XCTAssertEqual(try s.object(forKey: "string"), "one") } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [String].self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [String].self)) try s.setObject(["one", "two", "three"], forKey: "array of strings") XCTAssertEqual(try s.object(forKey: "array of strings"), ["one", "two", "three"]) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) try s.setObject(10, forKey: "int") XCTAssertEqual(try s.object(forKey: "int"), 10) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Int].self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Int].self)) try s.setObject([1, 2, 3], forKey: "array of ints") XCTAssertEqual(try s.object(forKey: "array of ints"), [1, 2, 3]) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Float.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Float.self)) let float: Float = 1.1 try s.setObject(float, forKey: "float") XCTAssertEqual(try s.object(forKey: "float"), float) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Float].self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Float].self)) let floats: [Float] = [1.1, 1.2, 1.3] try s.setObject(floats, forKey: "array of floats") XCTAssertEqual(try s.object(forKey: "array of floats"), floats) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Double.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Double.self)) let double: Double = 1.1 try s.setObject(double, forKey: "double") XCTAssertEqual(try s.object(forKey: "double"), double) } do { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [Double].self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Double].self)) let doubles: [Double] = [1.1, 1.2, 1.3] try s.setObject(doubles, forKey: "array of doubles") XCTAssertEqual(try s.object(forKey: "array of doubles"), doubles) @@ -83,7 +83,7 @@ final class StorageSupportTests: XCTestCase { } func testSetData() { - let s = storage.support(transformer: TransformerFactory.forData()) + let s = storage.transform(transformer: TransformerFactory.forData()) do { let string = "Hello" @@ -100,7 +100,7 @@ final class StorageSupportTests: XCTestCase { } func testSetDate() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Date.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Date.self)) let date = Date(timeIntervalSince1970: 100) try s.setObject(date, forKey: "date") @@ -110,7 +110,7 @@ final class StorageSupportTests: XCTestCase { } func testSetURL() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: URL.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: URL.self)) let url = URL(string: "https://hyper.no")! try s.setObject(url, forKey: "url") let cachedObject = try s.object(forKey: "url") @@ -119,14 +119,14 @@ final class StorageSupportTests: XCTestCase { } func testWithSet() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Set.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Set.self)) let set = Set(arrayLiteral: 1, 2, 3) try s.setObject(set, forKey: "set") XCTAssertEqual(try s.object(forKey: "set") as Set, set) } func testWithSimpleDictionary() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: [String: Int].self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [String: Int].self)) let dict: [String: Int] = [ "key1": 1, @@ -148,54 +148,38 @@ final class StorageSupportTests: XCTestCase { // try storage.setObject(dict, forKey: "dict") } - func testSameKey() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: User.self)) - - let user = User(firstName: "John", lastName: "Snow") - let key = "keyMadeOfDragonGlass" - try s.setObject(user, forKey: key) - - do { - let stringStorage = s.support(transformer: TransformerFactory.forCodable(ofType: String.self)) - try stringStorage.setObject("Dragonstone", forKey: key) - } - - XCTAssertNil(try? s.object(forKey: key)) - XCTAssertNotNil(try s.object(forKey: key)) - } - func testIntFloat() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Float.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Float.self)) let key = "key" try s.setObject(10, forKey: key) try then("Casting to int or float is the same") { XCTAssertEqual(try s.object(forKey: key), 10) - let intStorage = s.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let intStorage = s.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) XCTAssertEqual(try intStorage.object(forKey: key), 10) } } func testFloatDouble() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: Float.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Float.self)) let key = "key" try s.setObject(10.5, forKey: key) try then("Casting to float or double is the same") { XCTAssertEqual(try s.object(forKey: key), 10.5) - let doubleStorage = s.support(transformer: TransformerFactory.forCodable(ofType: Double.self)) + let doubleStorage = s.transform(transformer: TransformerFactory.forCodable(ofType: Double.self)) XCTAssertEqual(try doubleStorage.object(forKey: key), 10.5) } } func testCastingToAnotherType() throws { - let s = storage.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self)) try s.setObject("Hello", forKey: "string") do { - let intStorage = s.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let intStorage = s.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) let _ = try intStorage.object(forKey: "string") XCTFail() } catch { @@ -204,8 +188,8 @@ final class StorageSupportTests: XCTestCase { } func testOverriden() throws { - let intStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) - let stringStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: String.self)) + let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let stringStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self)) let key = "sameKey" diff --git a/Tests/iOS/Tests/Storage/SyncStorageTests.swift b/Tests/iOS/Tests/Storage/SyncStorageTests.swift index df14ad9..0934838 100644 --- a/Tests/iOS/Tests/Storage/SyncStorageTests.swift +++ b/Tests/iOS/Tests/Storage/SyncStorageTests.swift @@ -29,7 +29,7 @@ final class SyncStorageTests: XCTestCase { } func testRemoveAll() throws { - let intStorage = storage.support(transformer: TransformerFactory.forCodable(ofType: Int.self)) + let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) try given("add a lot of objects") { try Array(0..<100).forEach { try intStorage.setObject($0, forKey: "key-\($0)") From a890d781a936ad27951aee4469c3d0275cdd6e03 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:24:48 +0200 Subject: [PATCH 29/31] Add testOverridenOnDisk --- Tests/iOS/Tests/Storage/StorageSupportTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/iOS/Tests/Storage/StorageSupportTests.swift b/Tests/iOS/Tests/Storage/StorageSupportTests.swift index 41fa4b2..bc328ee 100644 --- a/Tests/iOS/Tests/Storage/StorageSupportTests.swift +++ b/Tests/iOS/Tests/Storage/StorageSupportTests.swift @@ -187,7 +187,7 @@ final class StorageSupportTests: XCTestCase { } } - func testOverriden() throws { + func testOverridenOnDisk() throws { let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self)) let stringStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self)) @@ -196,8 +196,8 @@ final class StorageSupportTests: XCTestCase { try intStorage.setObject(1, forKey: key) try stringStorage.setObject("hello world", forKey: key) - let intValue = try? intStorage.object(forKey: key) - let stringValue = try? stringStorage.object(forKey: key) + let intValue = try? intStorage.diskStorage.object(forKey: key) + let stringValue = try? stringStorage.diskStorage.object(forKey: key) XCTAssertNil(intValue) XCTAssertNotNil(stringValue) From d512b59aa02a28024d2295f2b34d4b0975e7b94c Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:27:31 +0200 Subject: [PATCH 30/31] Default to document directory --- Source/Shared/Storage/DiskStorage.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Shared/Storage/DiskStorage.swift b/Source/Shared/Storage/DiskStorage.swift index 420c20e..b5a9e7f 100644 --- a/Source/Shared/Storage/DiskStorage.swift +++ b/Source/Shared/Storage/DiskStorage.swift @@ -27,7 +27,10 @@ final public class DiskStorage { url = directory } else { url = try fileManager.url( - for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true + for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true ) } From 08ee51c899e1d39cfd61ad1736782a1347b44345 Mon Sep 17 00:00:00 2001 From: Khoa Pham Date: Wed, 13 Jun 2018 13:27:31 +0200 Subject: [PATCH 31/31] Default to document directory --- Source/Shared/Storage/DiskStorage.swift | 5 ++++- Tests/iOS/Tests/Storage/DiskStorageTests.swift | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Shared/Storage/DiskStorage.swift b/Source/Shared/Storage/DiskStorage.swift index 420c20e..b5a9e7f 100644 --- a/Source/Shared/Storage/DiskStorage.swift +++ b/Source/Shared/Storage/DiskStorage.swift @@ -27,7 +27,10 @@ final public class DiskStorage { url = directory } else { url = try fileManager.url( - for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true + for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true ) } diff --git a/Tests/iOS/Tests/Storage/DiskStorageTests.swift b/Tests/iOS/Tests/Storage/DiskStorageTests.swift index 6359149..94a09a1 100644 --- a/Tests/iOS/Tests/Storage/DiskStorageTests.swift +++ b/Tests/iOS/Tests/Storage/DiskStorageTests.swift @@ -30,7 +30,7 @@ final class DiskStorageTests: XCTestCase { /// Test that it returns the correct path func testDefaultPath() { let paths = NSSearchPathForDirectoriesInDomains( - .cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true + .documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true ) let path = "\(paths.first!)/\(config.name.capitalized)" XCTAssertEqual(storage.path, path)