elementsinteractive / ObjectStore

Convenient interface for persisting objects
BSD 3-Clause "New" or "Revised" License
4 stars 2 forks source link

Download

ObjectStore

Convenient interface for persisting objects.
implementation "nl.elements.objectstore:objectstore:+"
fun example(store: ObjectStore) {
    if ("id" !in store)
        store["id"] = 123L

    val id: Long = store["id"]

    store.remove("id")
}

Observing

Each ObjectStore is (Rx) observable and will emit whenever something changes in store.

fun observe(store: ObjectStore) {
    store
        .toObservable()
        .filter { event -> event.key == "id" }
        .map { event ->
            when (event) {
                is ObjectStore.Event.Updated -> store.get(event.key)
                is ObjectStore.Event.Removed -> -1L
            }
        }
        .subscribe(::println)
}

Encrypting

The store can be initialized with an adapter that can transform the incoming bytes into encrypted bytes and vice versa. By default there is no encryption enabled, but there is an implementation based on Facebook's Conceal included.

fun conceal(context: Context) {
    val keyChain = SharedPrefsBackedKeyChain(context, CryptoConfig.KEY_256)
    val crypto = AndroidConceal.get().createDefaultCrypto(keyChain)
    val prefs = context.getSharedPreferences("example", Context.MODE_PRIVATE)

    val store = PreferencesStore(
        preferences = prefs,
        transformer = ConcealTransformer(crypto)
    )
}

Aggregating

Each store has its own speciality (big values or a lot of small ones), but that is only interesting when you're writing to a store. When retrieving just want to query all the stores at once:

fun aggregate(directory: File, preferences: SharedPreferences) {
    // define the stores
    val pictures: ObjectStore = DirectoryStore(directory)
    val config: ObjectStore = PreferencesStore(preferences)

    // reduce them into one store
    val stores = listOf(pictures, config)
    val store: ReadableObjectStore = stores.reduce()

    // read from the stores
    val picture: Bitmap = store["selfie"]
    val token: String = store["token"]
}

The above method provides a read-only store, because it can not differentiate to which store it should persist to. If such functionality is desired, you can prefix your store:

fun aggregateWithNamespace(directory: File, preferences: SharedPreferences) {
    // define the stores
    val pictures: ObjectStore = DirectoryStore(directory)
    val config: ObjectStore = PreferencesStore(preferences)

    // reduce them into one store
    val stores = mapOf("pictures" to pictures, "config" to config)
    val store: ObjectStore = stores.reduceWithNamespace()

    // read from the stores
    val picture: Bitmap = store["pictures:selfie"]
    val token: String = store["config:token"]
}