Skip to content

Commit

Permalink
If cache not found in-memory, but found on disk, will set in-memory f…
Browse files Browse the repository at this point in the history
…rom disk before returning cache
  • Loading branch information
Kirmanie L Ravariere committed May 10, 2017
1 parent 960ff4e commit 9df0bfb
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 7 deletions.
36 changes: 29 additions & 7 deletions Source/Shared/BasicHybridCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,22 @@ public class BasicHybridCache: NSObject {
- Parameter name: A name of the cache
- Parameter config: Cache configuration
*/
public init(name: String, config: Config = Config.defaultConfig) {
public convenience init(name: String, config: Config = Config.defaultConfig) {
let frontStorage = StorageFactory.resolve(name, kind: config.frontKind, maxSize: UInt(config.maxObjects))
let backStorage = StorageFactory.resolve(name, kind: config.backKind, maxSize: config.maxSize)
self.init(name: name, frontStorage: frontStorage, backStorage: backStorage, config: config)

}
internal init(name: String, frontStorage: StorageAware, backStorage: StorageAware, config: Config) {
self.name = name
self.frontStorage = frontStorage
self.backStorage = backStorage
self.config = config

frontStorage = StorageFactory.resolve(name, kind: config.frontKind, maxSize: UInt(config.maxObjects))
backStorage = StorageFactory.resolve(name, kind: config.backKind, maxSize: config.maxSize)
super.init()

let notificationCenter = NotificationCenter.default

#if os(macOS)
notificationCenter.addObserver(self, selector: #selector(clearExpiredDataInBackStorage),
name: NSNotification.Name.NSApplicationWillTerminate, object: nil)
Expand All @@ -56,7 +62,7 @@ public class BasicHybridCache: NSObject {
name: .UIApplicationDidEnterBackground, object: nil)
#endif
}

/**
Removes notification center observer.
*/
Expand Down Expand Up @@ -109,10 +115,26 @@ public class BasicHybridCache: NSObject {
}

weakSelf.backStorage.object(key) { (object: T?) in
completion(object)
guard let object = object else {
completion(nil)
return
}
weakSelf.copyToFrontStorage(key, object: object, completion: completion)
}
}
}

private func copyToFrontStorage<T: Cachable>(_ key: String, object: T, completion: @escaping (_ object: T?) -> Void) {
self.backStorage.objectMetadata(key) { [weak self] (metadata: ObjectMetadata?) in
guard let weakSelf = self, let metadata = metadata else {
completion(nil)
return
}
weakSelf.frontStorage.add(key, object: object, expiry: metadata.expiry, completion: { _ in
completion(object)
})
}
}

/**
Removes the object from to the front and back cache storages.
Expand Down
24 changes: 24 additions & 0 deletions Source/Shared/Storage/DiskStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,30 @@ public final class DiskStorage: StorageAware {
completion?()
}
}

/**
Gets information about the cached object.
- Parameter key: Unique key to identify the object in the cache
- Parameter completion: Completion closure returns object metadata
*/
public func objectMetadata(_ key: String, completion: @escaping (_ metadata: ObjectMetadata?) -> Void) {
readQueue.async { [weak self] in
guard let weakSelf = self else {
completion(nil)
return
}

do {
let attributes = try weakSelf.fileManager.attributesOfItem(atPath: weakSelf.filePath(key))
let fileModifiedDate = Expiry.date(attributes[FileAttributeKey.modificationDate] as! Date)
completion(ObjectMetadata(expiry: fileModifiedDate))

} catch {
completion(nil)
}
}
}

/**
Tries to retrieve the object from the disk storage.
Expand Down
23 changes: 23 additions & 0 deletions Source/Shared/Storage/MemoryStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,29 @@ public final class MemoryStorage: StorageAware {
}
}

/**
Gets information about the cached object.
- Parameter key: Unique key to identify the object in the cache
- Parameter completion: Completion closure returns object metadata
*/
public func objectMetadata(_ key: String, completion: @escaping (_ metadata: ObjectMetadata?) -> Void) {
readQueue.async { [weak self] in
guard let weakSelf = self else {
completion(nil)
return
}

guard let capsule = weakSelf.cache.object(forKey: key as AnyObject) as? Capsule else {
completion(nil)
return
}

let expiryDate = Expiry.date(capsule.expiryDate)
completion(ObjectMetadata(expiry: expiryDate))
}
}

/**
Tries to retrieve the object from the memory storage.
Expand Down
28 changes: 28 additions & 0 deletions Tests/iOS/Specs/CacheSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,34 @@ class CacheSpec: QuickSpec {

self.waitForExpectations(timeout: 4.0, handler:nil)
}

it("should resolve from disk and set in-memory cache if object not in-memory") {
let frontStorage = MemoryStorage(name: "MemoryStorage")
let backStorage = DiskStorage(name: "DiskStorage")
let config = Config.defaultConfig
let key = "myusernamedjohn"
let object = SpecHelper.user

let cache = Cache<User>(name: "MyCache", frontStorage: frontStorage, backStorage: backStorage, config: config)

waitUntil(timeout: 4.0) { done in

backStorage.add(key, object: object) {

cache.object(key) { (receivedObject: User?) in

expect(receivedObject?.firstName).to(equal(object.firstName))
expect(receivedObject?.lastName).to(equal(object.lastName))

frontStorage.object(key) { (inmemoryCachedUser: User?) in
expect(inmemoryCachedUser?.firstName).to(equal(object.firstName))
expect(inmemoryCachedUser?.lastName).to(equal(object.lastName))
done()
}
}
}
}
}
}

describe("#remove") {
Expand Down

0 comments on commit 9df0bfb

Please sign in to comment.