Skip to content

Instantly share code, notes, and snippets.

@gilesvangruisen
Last active August 29, 2015 14:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gilesvangruisen/30c95af0d3ad372ecdc0 to your computer and use it in GitHub Desktop.
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.)
//
// 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
}
}
//
// 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)
}
}
}
}
//
// 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