Skip to content

๐Ÿ’พ Safe, statically-typed, store-agnostic key-value storage written in Swift!

License

Notifications You must be signed in to change notification settings

SwiftKitz/Storez

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

67 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Storez ๐Ÿ’พ

Safe, statically-typed, store-agnostic key-value storage

Version Version Swift Platforms Carthage

Highlights

  • Fully Customizable:
    Customize the persistence store, the KeyType class, post-commit actions .. Make this framework yours!

  • Batteries Included:
    In case you just want to use stuff, the framework is shipped with pre-configured basic set of classes that you can just use.

  • Portability, Check!:
    If you're looking to share code between you app and extensions, watchkit, apple watch, you're covered! You can use the NSUserDefaults store, just set your shared container identifier as the suite name.

Features

Available Stores

Store Backend Subspec
UserDefaultsStore NSUserDefaults Storez/UserDefaults
CacheStore NSCache Storez/Cache

For all stores, simply use pod "Storez"

Type-safe, store-agnostic, nestable Key definitions

// Entries must belong to a "group", for namespacing
struct Animals: Namespace {
    static let id = "animals"
}

let kingdom = Key<Animals, Void?>(id: "mammals", defaultValue: nil)
kingdom.stringValue // "animals:mammals"

// Nesting
struct Cats: Namespace {
    typealias parent = Animals
    static let id = "cats"

    // Namespaces also have pre and post commit hooks
    func preCommitHook() { /* custom code */ }
    func postCommitHook() { /* custom code */ }
}

let cat = Key<Cats, Void?>(id: "lion", defaultValue: nil)
cat.stringValue // "animals:cats:lion"

Initialize the store you want

// Use UserDefaultsStore for this example
let store = UserDefaultsStore(suite: "io.kitz.testing")
let key = Key<GlobalNamespace, Int?>(id: "key", defaultValue: nil)

// With three simple functions
store.set(key, value: 8)
store.get(key) // 8
store.clear() // Start fresh every time for testing

Optionality is honored throughout

let nullable = Key<GlobalNamespace, String?>(id: "nullable", defaultValue: nil)
store.get(nullable)?.isEmpty   // nil
store.set(nullable, value: "")
store.get(nullable)?.isEmpty   // true

let nonnull = Key<GlobalNamespace, String>(id: "nonnull", defaultValue: "!")
store.get(nonnull).isEmpty  // false
store.set(nonnull, value: "")
store.get(nonnull).isEmpty  // true

Custom objects easily supported

struct CustomObject {
    var strings: [String]
}

// You can guarentee they work for a specific store implementation by
// conforming to the store convertible protocol. In this case,
// NSUserDefaults requires the custom object can be converted to and
// from a supported Underlying type. (see UserDefaultsValueTypes.swift)
extension CustomObject: UserDefaultsConvertible {

    // We want to serialize this struct as NSString
    static func decode(userDefaultsValue value: NSString) -> CustomObject? {
        return self.init(strings: value.componentsSeparatedByString(";"))
    }

    var encodeForUserDefaults: NSString? {
        return strings.joinWithSeparator(";")
    }
}

// custom objects properly serialized/deserialized
let customObject = CustomObject(
    strings: ["fill", "in", "the"]
)

// let's add a processing block this time
let CustomValue = Key<GlobalNamespace, CustomObject?>(id: "custom", defaultValue: nil) {

    var processedValue = $0
    processedValue?.strings.append("blank!")
    return processedValue
}

store.set(CustomValue, value: customObject)
store.get(CustomValue)?.strings.joinWithSeparator(" ") // fill in the blank!

Make your own KeyType

// For example, make an key that emits NSNotifications
struct MyKey<G: Namespace, V>: KeyType {

    typealias NamespaceType = G
    typealias ValueType = V

    var stringValue: String
    var defaultValue: ValueType

    func didChange(oldValue: ValueType, newValue: ValueType) {
        NSNotificationCenter.defaultCenter().postNotificationName(stringValue, object: nil)
    }
}

Getting Started

Carthage

Carthage is fully supported. Simply add the following line to your Cartfile:

github "SwiftKitz/Storez"

CocoaPods

CocoaPods is fully supported. You can choose which store you want to use (see above). Simply add the following line to your Podfile:

pod 'Storez/UserDefaults'

Submodule

For manual installation, you can grab the source directly or through git submodules, then simply:

  • Drop the Storez.xcodeproj file as a subproject (make sure Copy resources is not enabled)
  • Navigate to your root project settings. Under "Embedded Binaries", click the "+" button and select the Storez.framework

Motivation

I've seen a lot of great attempts at statically-types data stores, but they all build a tightly coupled design that limits the end-developer's freedom. With this framework, you can start prototyping right away with the shipped features, then replace the persistence store and KeyType functionality with your heart's content and keep your code the way it is!

Author

Mazyod (@Mazyod)

License

Storez is released under the MIT license. See LICENSE for details.