Skip to content

Commit

Permalink
Add allObjects(), as suggested in #59
Browse files Browse the repository at this point in the history
  • Loading branch information
aschuch committed Mar 30, 2016
1 parent 544b35c commit 5defa16
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 29 deletions.
69 changes: 45 additions & 24 deletions AwesomeCache/Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,33 @@ public class Cache<T: NSCoding> {
///
/// - returns: The cached object for the given name, or nil
public func objectForKey(key: String) -> T? {
// Check if object exists in local cache
var possibleObject = cache.objectForKey(key) as? CacheObject
var object: CacheObject?

if possibleObject == nil {
// Try to load object from disk (synchronously)
dispatch_sync(queue) {
possibleObject = self.readObjectFromDisk(key)
}
dispatch_sync(queue) {
object = self.read(key)
}

// Check if object is not already expired and return
if let object = possibleObject where !object.isExpired() {
if let object = object where !object.isExpired() {
return object.value as? T
}

return nil
}

public func allObjects(includeExpired includeExpired: Bool = false) -> [T] {
var objects = [T]()

dispatch_sync(queue) {
let keys = self.allKeys()
let all = keys.map(self.read).flatMap { $0 }
let filtered = includeExpired ? all : all.filter { !$0.isExpired() }
objects = filtered.map { $0.value as? T }.flatMap { $0 }
}

return objects
}


// MARK: Set object

Expand All @@ -134,14 +144,8 @@ public class Cache<T: NSCoding> {
let expiryDate = expiryDateForCacheExpiry(expires)
let cacheObject = CacheObject(value: object, expiryDate: expiryDate)

// Set object in local cache
cache.setObject(cacheObject, forKey: key)

// Write object to disk (asyncronously)
dispatch_sync(queue) {
if let path = self.urlForKey(key).path {
NSKeyedArchiver.archiveRootObject(cacheObject, toFile: path)
}
self.add(cacheObject, key: key)
}
}

Expand All @@ -155,7 +159,7 @@ public class Cache<T: NSCoding> {
cache.removeObjectForKey(key)

dispatch_sync(queue) {
self.removeObjectFromDisk(key)
self.removeFromDisk(key)
}
}

Expand All @@ -165,7 +169,7 @@ public class Cache<T: NSCoding> {

dispatch_sync(queue) {
let keys = self.allKeys()
keys.forEach(self.removeObjectFromDisk)
keys.forEach(self.removeFromDisk)
}
}

Expand All @@ -178,10 +182,10 @@ public class Cache<T: NSCoding> {
let keys = self.allKeys()

for key in keys {
let possibleObject = self.readObjectFromDisk(key)
let possibleObject = self.read(key)
if let object = possibleObject where object.isExpired() {
self.cache.removeObjectForKey(key)
self.removeObjectFromDisk(key)
self.removeFromDisk(key)
}
}
}
Expand All @@ -204,20 +208,37 @@ public class Cache<T: NSCoding> {
}


// MARK: Private Helper
// MARK: Private Helper (not thread safe)

private func removeObjectFromDisk(key: String) {
let url = self.urlForKey(key)
_ = try? self.fileManager.removeItemAtURL(url)
private func add(object: CacheObject, key: String) {
// Set object in local cache
cache.setObject(object, forKey: key)

// Write object to disk
if let path = urlForKey(key).path {
NSKeyedArchiver.archiveRootObject(object, toFile: path)
}
}

private func readObjectFromDisk(key: String) -> CacheObject? {
private func read(key: String) -> CacheObject? {
// Check if object exists in local cache
if let object = cache.objectForKey(key) as? CacheObject {
return object
}

// Otherwise, read from disk
if let path = self.urlForKey(key).path where self.fileManager.fileExistsAtPath(path) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? CacheObject
}

return nil
}

// Deletes an object from disk
private func removeFromDisk(key: String) {
let url = self.urlForKey(key)
_ = try? self.fileManager.removeItemAtURL(url)
}

private func allKeys() -> [String] {
let urls = try? self.fileManager.contentsOfDirectoryAtURL(self.cacheDirectory, includingPropertiesForKeys: nil, options: [])
Expand Down
6 changes: 1 addition & 5 deletions AwesomeCache/CacheObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,12 @@ class CacheObject: NSObject, NSCoding {

required init?(coder aDecoder: NSCoder) {
guard let val = aDecoder.decodeObjectForKey("value"),
let expiry = aDecoder.decodeObjectForKey("expiryDate") as? NSDate else {
value = NSObject()
expiryDate = NSDate.distantPast()
super.init()
let expiry = aDecoder.decodeObjectForKey("expiryDate") as? NSDate else {
return nil
}

self.value = val
self.expiryDate = expiry

super.init()
}

Expand Down
31 changes: 31 additions & 0 deletions AwesomeCacheTests/AwesomeCacheTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import XCTest
@testable import AwesomeCache

class AwesomeCacheTests: XCTestCase {

func testCustomCachePath() {
let url = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first!
let cache = try! Cache<NSString>(name: "CustomCachePath", directory: url)

cache.setObject("AddedString", forKey: "add")
XCTAssertNotNil(cache.objectForKey("add"), "Get non-nil object")
}

func testGetterAndSetter() {
let cache = try! Cache<NSString>(name: "testGetterAndSetter")
Expand Down Expand Up @@ -107,6 +115,29 @@ class AwesomeCacheTests: XCTestCase {
XCTAssertNil(cache.objectForKey("2Seconds"), "Expires in 2 seconds")
XCTAssertNil(cache.objectForKey("atDate"), "Expires in 3 seconds")
}

func testAllObjects() {
let cache = try! Cache<NSString>(name: "testAllObjects")

cache.setObject("NeverExpires", forKey: "never", expires: .Never)
cache.setObject("ExpiresIn2Seconds", forKey: "2Seconds", expires: .Seconds(2))
cache.setObject("ExpiresAtDate", forKey: "atDate", expires: .Date(NSDate().dateByAddingTimeInterval(4)))

sleep(2)

let all = cache.allObjects()

XCTAssertTrue(all.count == 2, "2 returned objects")
XCTAssertTrue(all.contains("NeverExpires"), "Never expires")
XCTAssertFalse(all.contains("ExpiresIn2Seconds"), "Expires in 2 seconds")
XCTAssertTrue(all.contains("ExpiresAtDate"), "Expires in 4 seconds")

let expiredIncluded = cache.allObjects(includeExpired: true)
XCTAssertTrue(expiredIncluded.count == 3, "3 returned objects")
XCTAssertTrue(expiredIncluded.contains("NeverExpires"), "Never expires")
XCTAssertTrue(expiredIncluded.contains("ExpiresIn2Seconds"), "Expires in 2 seconds")
XCTAssertTrue(expiredIncluded.contains("ExpiresAtDate"), "Expires in 4 seconds")
}

func testCacheBlockExecuted() {
let cache = try! Cache<NSString>(name: "testCacheBlockExecuted")
Expand Down

0 comments on commit 5defa16

Please sign in to comment.