Code Monkey home page Code Monkey logo

stores's Introduction

๐Ÿ—‚ Stores

Stores codecov License

A typed key-value storage solution to store Codable types in various persistence layers like User Defaults, File System, Core Data, Keychain, and more with a few lines of code!

Features

  • macOS Catalina+, iOS 13+, tvOS 13+, watchOS 6+, any Linux supporting Swift 5.4+.
  • Store any Codable type.
  • Single API with implementations using User Default, file system, Core Data, Keychain, and fakes for testing.
  • Thread-safe implementations.
  • Swappable implementations with a type-erased store.
  • Modular library, add all, or only what you need.

Motivation

When working on an app that uses the composable architecture, I fell in love with how reducers use an environment type that holds any dependencies the feature needs, such as API clients, analytics clients, and more.

Stores tries to abstract the concept of a store and provide various implementations that can be injected in such environment and swapped easily when running tests or based on a remote flag.

It all boils down to the two protocols SingleObjectStore and MultiObjectStore defined in the Blueprints layer, which provide the abstract concepts of stores that can store a single or multiple objects of a generic Codable type.

The two protocols are then implemented in the different modules as explained in the chart below:

Modules chart Modules chart


Usage

Let's say you have a User struct defined as below:

struct User: Codable {
    let id: Int
    let name: String
}

Here's how you store it using Stores:

1. Conform to Identifiable

This is required to make the store associate an object with its id.

extension User: Identifiable {}

The property id can be on any Hashable type. Read more.

2. Create a store

Stores comes pre-equipped with the following stores:

  • UserDefaults
    // Store for multiple objects
    let store = MultiUserDefaultsStore<User>(suiteName: "users")
    
    // Store for a single object
    let store = SingleUserDefaultsStore<User>(suiteName: "users")
  • FileSystem
    // Store for multiple objects
    let store = MultiFileSystemStore<User>(path: "users")
    
    // Store for a single object
    let store = SingleFileSystemStore<User>(path: "users")
  • CoreData
    // Store for multiple objects
    let store = MultiCoreDataStore<User>(databaseName: "users")
    
    // Store for a single object
    let store = SingleCoreDataStore<User>(databaseName: "users")
  • Keychain
    // Store for multiple objects
    let store = MultiKeychainStore<User>(identifier: "users")
    
    // Store for a single object
    let store = SingleKeychainStore<User>(identifier: "users")
  • Fakes (for testing)
    // Store for multiple objects
    let store = MultiObjectStoreFake<User>()
    
    // Store for a single object
    let store = SingleObjectStoreFake<User>()

You can create a custom store by implementing the protocols in Blueprints

  • Realm
    // Store for multiple objects
    final class MultiRealmStore<Object: Codable & Identifiable>: MultiObjectStore {
        // ...
    }
    
    // Store for a single object
    final class SingleRealmStore<Object: Codable>: SingleObjectStore {
        // ...
    }
  • SQLite
    // Store for multiple objects
    final class MultiSQLiteStore<Object: Codable & Identifiable>: MultiObjectStore {
        // ...
    }
    
    // Store for a single object
    final class SingleSQLiteStore<Object: Codable>: SingleObjectStore {
        // ...
    }
3. Inject the store

Assuming we have a view model that uses a store to fetch data:

struct UsersViewModel {
    let store: AnyMultiObjectStore<User>
}

Inject the appropriate store implementation:

let coreDataStore = MultiCoreDataStore<User>(databaseName: "users")
let prodViewModel = UsersViewModel(store: coreDataStore.eraseToAnyStore())

or:

let fakeStore = MultiObjectStoreFake<User>()
let testViewModel = UsersViewModel(store: fakeStore.eraseToAnyStore())
4. Save, retrieve, update, or remove objects
let john = User(id: 1, name: "John Appleseed")

// Save an object to a store
try store.save(john)

// Save an array of objects to a store
try store.save([jane, steve, jessica])

// Get an object from store
let user = store.object(withId: 1)

// Get an array of object in store
let users = store.objects(withIds: [1, 2, 3])

// Get an array of all objects in store
let allUsers = store.allObjects()

// Check if store has an object
print(store.containsObject(withId: 10)) // false

// Remove an object from a store
try store.remove(withId: 1)

// Remove multiple objects from a store
try store.remove(withIds: [1, 2, 3])

// Remove all objects in a store
try store.removeAll()

Documentation

Read the full documentation at Swift Package Index.


Installation

You can add Stores to an Xcode project by adding it as a package dependency.

  1. From the File menu, select Add Packages...
  2. Enter "https://github.com/omaralbeik/Stores" into the package repository URL text field
  3. Depending on what you want to use Stores for, add the following target(s) to your app:
    • Stores: the entire library with all stores.
    • UserDefaultsStore: use User Defaults to persist data.
    • FileSystemStore: persist data by saving it to the file system.
    • CoreDataStore: use a Core Data database to persist data.
    • KeychainStore: persist data securely in the Keychain.
    • Blueprints: protocols only, this is a good option if you do not want to use any of the provided stores and build yours.
    • StoresTestUtils to use the fakes in your tests target.

Credits and thanks


License

Stores is released under the MIT license. See LICENSE for more information.

stores's People

Contributors

devagani avatar jordanekay avatar omaralbeik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

stores's Issues

CoreDataStore with AppGroup

Hi, i want to store the CoreData Container with a custom URL to share it with a widget extension.

Here is my code:

let accessToken = SingleCoreDataStore<String>(databaseName: "accessToken") { objectModel in
        let container = NSPersistentContainer(name: "accessToken")
        let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.justtreme.teamplaner")!.appendingPathComponent("accessToken.sqlite")
        let description = NSPersistentStoreDescription(url: storeURL)
        container.persistentStoreDescriptions = [description]

        container.loadPersistentStores { (_, error) in
            if let error = error {
                print(error.localizedDescription)
            }
        }
        
        return container
    }

I can store the data and retrieve it from the iOS app but it is not available on the widget extension.
This code is in a handler file wich is available for both targets.

Can someone help me with that?
Ferris

iCloud Sync

Since a CoreData store exist would it be possible to add an option to use NSPersistentCloudKitContainer or an external solution like SyncKit ?

Can't delete from KeyChainStore

Whenever I want to delete a value from the MultiKeyChainStore I get the error:
KeyChainStore.KeyChainError-Error 0.

This is the code im using:

do {
    try keyChainStore.removeAll()
} catch {
    print(error.localizedDescription)
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.