Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to generic Storage #189

Merged
merged 32 commits into from
Jun 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
beedc30
Add generic Storage
onmyway133 Jun 13, 2018
0b8f5c3
Add HybridStorage2
onmyway133 Jun 13, 2018
7e9ffad
Add function to support types
onmyway133 Jun 13, 2018
187035b
Add support on SyncStorage2
onmyway133 Jun 13, 2018
aa664e1
Add Storage2
onmyway133 Jun 13, 2018
4c48dfb
Add AsyncStorage2
onmyway133 Jun 13, 2018
760d267
Add AsyncStorage2
onmyway133 Jun 13, 2018
d3e9a71
Expose all public
onmyway133 Jun 13, 2018
56c4483
Add factory methods to Transformer
onmyway133 Jun 13, 2018
12abe4f
Add Transformer for Codable and for Data
onmyway133 Jun 13, 2018
74f9794
Fix type annotation
onmyway133 Jun 13, 2018
9cbd6bd
Use TypeWrapper to work with primitive types
onmyway133 Jun 13, 2018
4346ba4
Remove old Storage files
onmyway133 Jun 13, 2018
6e468a1
Rename to Storage
onmyway133 Jun 13, 2018
d38c941
Readd totalCostLimit
onmyway133 Jun 13, 2018
be3a40c
Update MemoryStorageTests
onmyway133 Jun 13, 2018
fb823e9
Add TransformerFactory
onmyway133 Jun 13, 2018
efdf1c7
Rename to StorageAware
onmyway133 Jun 13, 2018
6a81af8
Use TransformerFactory
onmyway133 Jun 13, 2018
00bded4
Update DiskStorageTests
onmyway133 Jun 13, 2018
e69d029
Update HybridStorageTests
onmyway133 Jun 13, 2018
e96da46
Expose all Storage classes
onmyway133 Jun 13, 2018
02245ec
Update SyncStorageTests
onmyway133 Jun 13, 2018
ac0cedb
Update AsyncStorageTests
onmyway133 Jun 13, 2018
0aff00c
Add tests for Data, Date, URL
onmyway133 Jun 13, 2018
3390ee2
Add test for overriden
onmyway133 Jun 13, 2018
385371e
Add testOverriden
onmyway133 Jun 13, 2018
40a8b30
Rename from support to transform
onmyway133 Jun 13, 2018
a890d78
Add testOverridenOnDisk
onmyway133 Jun 13, 2018
d512b59
Default to document directory
onmyway133 Jun 13, 2018
08ee51c
Default to document directory
onmyway133 Jun 13, 2018
47b19a1
Merge branch 'refactor/generic_storage' of https://github.com/hyperos…
onmyway133 Jun 13, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 138 additions & 100 deletions Cache.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

18 changes: 8 additions & 10 deletions Source/Shared/Configuration/MemoryConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
self.expiry = expiry
self.countLimit = countLimit
}
/// 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

// 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.expiry = expiry
self.countLimit = countLimit
self.totalCostLimit = totalCostLimit
}
}
2 changes: 1 addition & 1 deletion Source/Shared/Library/Entry.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// A wrapper around cached object and its expiry date.
public struct Entry<T: Codable> {
public struct Entry<T> {
/// Cached object
public let object: T
/// Expiry date
Expand Down
20 changes: 20 additions & 0 deletions Source/Shared/Library/MemoryCapsule.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
11 changes: 11 additions & 0 deletions Source/Shared/Library/Optional+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

public extension Optional {
func unwrapOrThrow(error: Error) throws -> Wrapped {
if let value = self {
return value
} else {
throw error
}
}
}
2 changes: 2 additions & 0 deletions Source/Shared/Library/StorageError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
11 changes: 11 additions & 0 deletions Source/Shared/Library/Transformer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

public class Transformer<T> {
let toData: (T) throws -> Data
let fromData: (Data) throws -> T

public init(toData: @escaping (T) throws -> Data, fromData: @escaping (Data) throws -> T) {
self.toData = toData
self.fromData = fromData
}
}
38 changes: 38 additions & 0 deletions Source/Shared/Library/TransformerFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Foundation

public class TransformerFactory {
public static func forData() -> Transformer<Data> {
let toData: (Data) throws -> Data = { $0 }

let fromData: (Data) throws -> Data = { $0 }

return Transformer<Data>(toData: toData, fromData: fromData)
}

public static func forImage() -> Transformer<Image> {
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<Image>(toData: toData, fromData: fromData)
}

public static func forCodable<U: Codable>(ofType: U.Type) -> Transformer<U> {
let toData: (U) throws -> Data = { object in
let wrapper = TypeWrapper<U>(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<U>.self, from: data).object
}

return Transformer<U>(toData: toData, fromData: fromData)
}
}
14 changes: 14 additions & 0 deletions Source/Shared/Library/TypeWrapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation

/// Used to wrap Codable object
public struct TypeWrapper<T: Codable>: Codable {
enum CodingKeys: String, CodingKey {
case object
}

public let object: T

public init(object: T) {
self.object = object
}
}
60 changes: 45 additions & 15 deletions Source/Shared/Storage/AsyncStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ 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 class AsyncStorage<T> {
public let innerStorage: HybridStorage<T>
public let serialQueue: DispatchQueue

init(storage: StorageAware, serialQueue: DispatchQueue) {
self.internalStorage = storage
public init(storage: HybridStorage<T>, serialQueue: DispatchQueue) {
self.innerStorage = storage
self.serialQueue = serialQueue
}
}

extension AsyncStorage: AsyncStorageAware {
public func entry<T>(ofType type: T.Type, forKey key: String, completion: @escaping (Result<Entry<T>>) -> Void) {
extension AsyncStorage {
public func entry(forKey key: String, completion: @escaping (Result<Entry<T>>) -> 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)
let anEntry = try self.innerStorage.entry(forKey: key)
completion(Result.value(anEntry))
} catch {
completion(Result.error(error))
Expand All @@ -38,26 +38,27 @@ extension AsyncStorage: AsyncStorageAware {
}

do {
try self.internalStorage.removeObject(forKey: key)
try self.innerStorage.removeObject(forKey: key)
completion(Result.value(()))
} catch {
completion(Result.error(error))
}
}
}

public func setObject<T: Codable>(_ object: T,
forKey key: String,
expiry: Expiry? = nil,
completion: @escaping (Result<()>) -> Void) {
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)
try self.innerStorage.setObject(object, forKey: key, expiry: expiry)
completion(Result.value(()))
} catch {
completion(Result.error(error))
Expand All @@ -73,7 +74,7 @@ extension AsyncStorage: AsyncStorageAware {
}

do {
try self.internalStorage.removeAll()
try self.innerStorage.removeAll()
completion(Result.value(()))
} catch {
completion(Result.error(error))
Expand All @@ -89,11 +90,40 @@ extension AsyncStorage: AsyncStorageAware {
}

do {
try self.internalStorage.removeExpiredObjects()
try self.innerStorage.removeExpiredObjects()
completion(Result.value(()))
} catch {
completion(Result.error(error))
}
}
}

public func object(forKey key: String, completion: @escaping (Result<T>) -> Void) {
entry(forKey: key, completion: { (result: Result<Entry<T>>) in
completion(result.map({ entry in
return entry.object
}))
})
}

public func existsObject(
forKey key: String,
completion: @escaping (Result<Bool>) -> Void) {
object(forKey: key, completion: { (result: Result<T>) in
completion(result.map({ _ in
return true
}))
})
}
}

public extension AsyncStorage {
func transform<U>(transformer: Transformer<U>) -> AsyncStorage<U> {
let storage = AsyncStorage<U>(
storage: innerStorage.transform(transformer: transformer),
serialQueue: serialQueue
)

return storage
}
}
82 changes: 0 additions & 82 deletions Source/Shared/Storage/AsyncStorageAware.swift

This file was deleted.

Loading