Skip to content

Instantly share code, notes, and snippets.

@nrubin29
Last active October 15, 2017 21:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nrubin29/049b27ad5224699d6f30932bd4babf68 to your computer and use it in GitHub Desktop.
Save nrubin29/049b27ad5224699d6f30932bd4babf68 to your computer and use it in GitHub Desktop.
A small Swift library that provides a persistent data store using NSCoding.
//
// 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