Last active
August 29, 2015 14:17
-
-
Save gilesvangruisen/30c95af0d3ad372ecdc0 to your computer and use it in GitHub Desktop.
StorableValue protocol for encoding/decoding a value type and StoredValue object for boxing up a StorableValue to be cached. (Value types can't conform to NSCoding because it's a class-protocol.)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// LocalStore.swift | |
// FPAPI | |
// | |
// Created by Giles Van Gruisen on 3/23/15. | |
// Copyright (c) 2015 Remarkable.io. All rights reserved. | |
// | |
import Foundation | |
import AwesomeCache | |
import Deferred | |
public struct LocalStore<T: StorableValue> { | |
let cache: Cache<NSData> | |
let storeQueue: dispatch_queue_t | |
public init(name: String) { | |
self.cache = Cache<NSData>(name: name) | |
self.storeQueue = dispatch_queue_create("com.fashionProject.fpapi.\(name)Cache", DISPATCH_QUEUE_SERIAL) | |
} | |
public func valueForKey(key: String) -> Deferred<T> { | |
// Defer and return immediately because AwesomeCache is synchronous | |
let deferred = Deferred<T>() | |
// Make async (AwesomeCache should really handle async | |
dispatch_async(self.storeQueue) { | |
// Perform cache lookup | |
let possibleValueData = self.cache.objectForKey(key) | |
// Check for value | |
if let valueData = possibleValueData { | |
// Value found -> decode and resolve | |
let value = StoredValue<T>(data: valueData).decode() | |
deferred.resolve(value) | |
} else { | |
// No value found -> reject | |
deferred.reject(NSError(domain: "LocalStore", code: 0, userInfo: ["EmptyKey": key])) | |
} | |
} | |
return deferred | |
} | |
public func setValueForKey(value: T, key: String) -> Deferred<T> { | |
// Defer and return immediately because AwesomeCache is synchronous | |
let deferred = Deferred<T>() | |
// Build StoredValue to convert to data | |
let storedValueData = StoredValue<T>(value: value).data | |
// TODO: Update AwesomeCache to give write feedback instead of blindly resolving | |
self.cache.setObject(storedValueData, forKey: key) | |
// Resolve with saved value | |
deferred.resolve(value) | |
return deferred | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// LocalStoreSpec.swift | |
// FPAPI | |
// | |
// Created by Giles Van Gruisen on 3/23/15. | |
// Copyright (c) 2015 Remarkable.io. All rights reserved. | |
// | |
import Foundation | |
import FPAPI | |
import Deferred | |
import Quick | |
import Nimble | |
// TODO: Replace with Product | |
// Simple model for testing | |
struct Person { | |
let name: String | |
static func create(name: String) -> Person { | |
return Person(name: name) | |
} | |
} | |
extension Person: StorableValue { | |
static func decode(decoder: NSCoder) -> Person { | |
return Person(name: decoder.decodeObjectForKey("name") as String) | |
} | |
func encode(coder: NSCoder) { | |
coder.encodeObject(name, forKey: "name") | |
} | |
} | |
class LocalStoreSpec: QuickSpec { | |
// Passing LocalStore set & get test | |
override func spec() { | |
describe("Setting and getting from LocalStore") { | |
let store = LocalStore<Person>(name: "PersonStore") | |
let personValue = Person(name: "Giles") // sample Person | |
store.setValueForKey(personValue, key: "person") | |
let deferredPerson = store.valueForKey("person") // returns Deferred<Person> | |
var personFromCache = Person(name: "Someone") | |
deferredPerson.then({ (person: Person) -> () in | |
personFromCache = person | |
}) | |
it("should return the same person") { | |
expect(personFromCache.name).toEventually(equal("Giles"), timeout: 1, pollInterval: 0.1) | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// StoredValue.swift | |
// FPAPI | |
// | |
// Created by Giles Van Gruisen on 3/20/15. | |
// Copyright (c) 2015 Remarkable.io. All rights reserved. | |
// | |
import Foundation | |
/** | |
The StorableValue protocol declares two functions | |
for encoding and decoding a Swift value type | |
because NSCoding is restricted to class types. | |
*/ | |
public protocol StorableValue { | |
class func decode(decoder: NSCoder) -> Self | |
func encode(coder: NSCoder) | |
} | |
/** | |
StoredValue defines a generic class defines a | |
generic class used to encode/decode and box data | |
from any StorableValue to make caching easier. | |
*/ | |
final public class StoredValue<T: StorableValue> { | |
private var mutableData = NSMutableData() | |
public var data: NSData { | |
get { | |
return NSData(data: mutableData) | |
} | |
} | |
public init(value: T) { | |
encode(value) | |
} | |
public init(data: NSData) { | |
mutableData = NSMutableData(data: data) | |
} | |
// Set value | |
func encode(value: T) { | |
let archiver = NSKeyedArchiver(forWritingWithMutableData: mutableData) | |
value.encode(archiver) | |
archiver.finishEncoding() | |
} | |
// Access value | |
func decode() -> T { | |
let unarchiver = NSKeyedUnarchiver(forReadingWithData: mutableData) | |
let value = T.decode(unarchiver) | |
unarchiver.finishDecoding() | |
return value | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment