Last active
October 15, 2017 21:14
-
-
Save nrubin29/049b27ad5224699d6f30932bd4babf68 to your computer and use it in GitHub Desktop.
A small Swift library that provides a persistent data store using NSCoding.
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
// | |
// DataStore.swift | |
// A persistent key-value store. | |
// | |
// Created by Noah Rubin on 6/29/16. | |
// Copyright © 2016 nrubin29. All rights reserved. | |
// | |
import Foundation | |
/// A class which represents a collection of keys and values to be stored. | |
public class DataStore { | |
/// The singleton instance of the data store. | |
public static let instance: DataStore = DataStore() | |
var delegate: DataStoreSaveDelegate? | |
private var data: [DataPoint] | |
public var keys: [String] { | |
get { | |
return data.map { $0.key } | |
} | |
} | |
public var values: [AnyObject] { | |
get { | |
return data.map { $0.value } | |
} | |
} | |
public var dictionary: [String: AnyObject] { | |
get { | |
var dict: [String: AnyObject] = [:] | |
for point in data { | |
dict[point.key] = point.value | |
} | |
return dict | |
} | |
} | |
private init() { | |
if let d = NSKeyedUnarchiver.unarchiveObject(withFile: DataPoint.ArchiveURL.path) { | |
self.data = d as AnyObject as! [DataPoint] | |
} | |
else { | |
self.data = [] | |
} | |
} | |
/** | |
Gets the value associated with the given key. | |
- Parameters: | |
- T: The type of value to return. | |
- key: The key. | |
- Returns: The value associated with the key, or nil if the key is not present. | |
*/ | |
public func get<T>(_ key: String) -> T? { | |
for point in data { | |
if point.key == key { | |
return point.value as? T | |
} | |
} | |
return nil | |
} | |
/** | |
Gets the value associated with the given key, or gives the default value if the key is not present. | |
- Parameters: | |
- T: The type of value to return. | |
- key: The key. | |
- defaultValue: The default value to be returned if the key is not present. | |
- Returns: The value associated with the key, or the default value if the key is not present. | |
*/ | |
public func get<T>(_ key: String, defaultValue: T) -> T { | |
return get(key) ?? defaultValue | |
} | |
/** | |
Puts a given key and value into the store, or sets the given key to the value if the key is already present. | |
- Parameters: | |
- key: The key. | |
- value: The value. | |
*/ | |
public func put(_ key: String, value: AnyObject) { | |
var found = false | |
for point in data { | |
if point.key == key { | |
point.value = value | |
found = true | |
break | |
} | |
} | |
if !found { | |
data.append(DataPoint(key: key, value: value)) | |
} | |
save() | |
} | |
/** | |
Removes the given key from the store, returning the value if the key was present. | |
- Parameters: | |
- key: The key. | |
- Returns: The value for the removed key, if the key was present. | |
*/ | |
public func remove(_ key: String) -> AnyObject? { | |
for (i, point) in data.enumerated().reversed() { | |
if point.key == key { | |
let temp = data.remove(at: i) | |
save() | |
return temp | |
} | |
} | |
return nil | |
} | |
/** | |
Clears all values from the store. | |
*/ | |
public func clear() { | |
self.data = [] | |
save() | |
} | |
/** | |
Returns the value for the given key, or sets the value of a key, or removes a key. | |
- Parameters: | |
- key: The key. | |
- newValue: A new value to be set for the key. If it is nil, the key is removed from the store. | |
- Returns: The value for the key, if the get subscript is used. | |
*/ | |
subscript(_ key: String) -> AnyObject? { | |
get { | |
return get(key) | |
} | |
set { | |
if let v = newValue { | |
put(key, value: v) | |
} | |
else { | |
remove(key) | |
} | |
} | |
} | |
// subscript(index: Int) -> AnyObject? { | |
// get { | |
// return (data[index].key, data[index].value) | |
// } | |
// | |
// set { | |
// data[index] = DataPoint(newValue[0], newValue[1]) | |
// } | |
// } | |
private func save() { | |
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(NSMutableArray(array: data), toFile: DataPoint.ArchiveURL.path) | |
if !isSuccessfulSave { | |
delegate?.saveFailed() | |
} | |
} | |
} | |
/// A class which represents a key-value pair for the data store. | |
@objc(DataPoint) private class DataPoint: NSObject, NSCoding { | |
var key: String | |
var value: AnyObject | |
@objc func encode(with aCoder: NSCoder) { | |
aCoder.encode(key, forKey: "key") | |
aCoder.encode(value, forKey: "value") | |
} | |
@objc required convenience init?(coder aDecoder: NSCoder) { | |
let k = aDecoder.decodeObject(forKey: "key") as! String | |
let v = aDecoder.decodeObject(forKey: "value")! | |
self.init(key: k, value: v as AnyObject) | |
} | |
init(key: String, value: AnyObject) { | |
self.key = key | |
self.value = value | |
super.init() | |
} | |
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first! | |
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("data") | |
} | |
/// A protocol that handles save failures. | |
public protocol DataStoreSaveDelegate { | |
/// A function that is called when a save fails. | |
func saveFailed() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment