diff --git a/Images/friendly_robot.png b/Images/friendly_robot.png
new file mode 100644
index 0000000..424a3f7
Binary files /dev/null and b/Images/friendly_robot.png differ
diff --git a/README.md b/README.md
index c004806..233ff76 100644
--- a/README.md
+++ b/README.md
@@ -1,362 +1,123 @@
-# Store 5
+
+
+
Store5
+
-## Why We Made Store
+
+
Full documentation can be found on our website!
+
-- Modern software needs data representations to be fluid and always available.
-- Users expect their UI experience to never be compromised (blocked) by new data loads. Whether an
- application is social, news or business-to-business, users expect a seamless experience both
- online and offline.
-- International users expect minimal data downloads as many megabytes of downloaded data can quickly
- result in astronomical phone bills.
+### Concepts
-Store is a Kotlin library for loading data from remote and local sources.
+- [Store](https://mobilenativefoundation.github.io/Store/store/store/) is a typed repository that returns a flow
+ of [Data](https://github.com/MobileNativeFoundation/Store/blob/main/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt#L39)
+ /[Loading](https://github.com/MobileNativeFoundation/Store/blob/main/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt#L34)
+ /[Error](https://github.com/MobileNativeFoundation/Store/blob/main/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt#L51)
+ from local and network data sources
+- [MutableStore](https://mobilenativefoundation.github.io/Store/mutable-store/building/overview/) is a mutable repository implementation that allows create **(C)**, read **(R)**,
+ update **(U)**, and delete **(D)** operations for local and network resources
+- [SourceOfTruth](https://mobilenativefoundation.github.io/Store/mutable-store/building/implementations/source-of-truth/) persists items
+- [Fetcher](https://mobilenativefoundation.github.io/Store/mutable-store/building/implementations/fetcher/) defines how data will be fetched over network
+- [Updater](https://mobilenativefoundation.github.io/Store/mutable-store/building/implementations/updater/) defines how local changes will be pushed to network
+- [Bookkeeper](https://mobilenativefoundation.github.io/Store/mutable-store/building/implementations/bookkeeper/) tracks metadata of local changes and records
+ synchronization failures
+- [Validator](https://mobilenativefoundation.github.io/Store/mutable-store/building/implementations/validator/) returns whether an item is valid
+- [Converter](https://mobilenativefoundation.github.io/Store/mutable-store/building/implementations/converter/) converts items
+ between [Network](https://mobilenativefoundation.github.io/Store/mutable-store/building/generics/network)
+ /[Local](https://mobilenativefoundation.github.io/Store/mutable-store/building/generics/sot)
+ /[Output](https://mobilenativefoundation.github.io/Store/mutable-store/building/generics/common) representations
-### Overview
+### Including Store In Your Project
-A Store is responsible for managing a particular data request. When you create an implementation of
-a Store, you provide it with a `Fetcher`, a function that defines how data will be fetched over
-network. You can also define how your Store will cache data in-memory and on-disk. Since Store
-returns your data as a `Flow`, threading is a breeze! Once a Store is built, it handles the logic
-around data flow, allowing your views to use the best data source and ensuring that the newest data
-is always available for later offline use.
-
-Store leverages multiple request throttling to prevent excessive calls to the network and disk
-cache. By utilizing Store, you eliminate the possibility of flooding your network with the same
-request while adding two layers of caching (memory and disk) as well as ability to add disk as a
-source of truth where you can modify the disk directly without going through Store (works best with
-databases that can provide observables sources
-like [Jetpack Room](https://developer.android.com/jetpack/androidx/releases/room)
-, [SQLDelight](https://github.com/cashapp/sqldelight)
-or [Realm](https://realm.io/products/realm-database/))
-
-### How to include in your project
-
-Artifacts are hosted on **Maven Central**.
+#### Android
```kotlin
-STORE_VERSION = "5.0.0-alpha03"
+implementation "org.mobilenativefoundation.store:store5:5.0.0-alpha03"
```
-### Android
-
-```groovy
-implementation "org.mobilenativefoundation.store:store5:$STORE_VERSION"
-```
-
-### Multiplatform (Common, JVM, Native, JS)
+#### Multiplatform (Common, JVM, Native, JS)
```kotlin
commonMain {
dependencies {
- implementation("org.mobilenativefoundation.store:store5:$STORE_VERSION")
+ implementation("org.mobilenativefoundation.store:store5:5.0.0-alpha03")
}
}
```
-### Fully Configured Store
+### Getting Started
-Let's start by looking at what a fully configured Store looks like. We will then walk through
-simpler examples showing each piece:
+#### Building Your First Store
```kotlin
StoreBuilder
- .from(
- fetcher = Fetcher.of { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
- sourceOfTruth = SourceOfTruth.of(
- reader = db.postDao()::loadPosts,
- writer = db.postDao()::insertPosts,
- delete = db.postDao()::clearFeed,
- deleteAll = db.postDao()::clearAllFeeds
- )
- ).build()
+ .from(fetcher, sourceOfTruth)
+ .converter(converter)
+ .validator(validator)
+ .build(updater, bookkeeper)
```
-With the above setup you have:
+#### Creating
-+ In-memory caching for rotation
-+ Disk caching for when users are offline
-+ Throttling of API calls when parallel requests are made for the same resource
-+ Rich API to ask for data whether you want cached, new or a stream of future data updates.
-
-And now for the details:
-
-### Creating a Store
-
-You create a Store using a builder. The only requirement is to include a `Fetcher` which is just
-a `typealias` to a function that returns a `Flow>`.
+##### Request
```kotlin
-val store = StoreBuilder
- .from(Fetcher.ofFlow { articleId -> api.getArticle(articleId) }) // api returns Flow
- .build()
+store.write(
+ request = StoreWriteRequest.of(
+ key = key,
+ value = value
+ )
+)
```
-Store uses generic keys as identifiers for data. A key can be any value object that properly
-implements `toString()`, `equals()` and `hashCode()`. When your `Fetcher` function is called, it
-will be passed a particular `Key` value. Similarly, the key will be used as a primary identifier
-within caches (Make sure to have a proper `hashCode()`!!).
+##### Response
-Note: We highly recommend using built-in types that implement `equals` and `hashcode` or
-Kotlin `data` classes for complex keys.
+```text
+1. StoreWriteResponse.Success.Typed(response)
+```
-### Public Interface - Stream
+#### Reading
-The primary function provided by a `Store` instance is the `stream` function which has the following
-signature:
+##### Request
```kotlin
-fun stream(request: StoreRequest): Flow>
+store.stream(request = StoreReadRequest.cached(key, refresh = false))
```
-Each `stream` call receives a `StoreRequest` object, which defines which key to fetch and which data
-sources to utilize.
-The response is a `Flow` of `StoreResponse`. `StoreResponse` is a Kotlin sealed class that can be
-either
-a `Loading`, `Data` or `Error` instance.
-Each `StoreResponse` includes an `origin` field which specifies where the event is coming from.
+##### Response
-* The `Loading` class only has an `origin` field. This can provide you information like "network is
- fetching data", which can be a good signal to activate the loading spinner in your UI.
-* The `Data` class has a `value` field which includes an instance of the type returned by `Store`.
-* The `Error` class includes an `error` field that contains the exception thrown by the
- given `origin`.
+```text
+1. StoreReadResponse.Data(value, origin = StoreReadResponseOrigin.Cache)
+```
-When an error happens, `Store` does not throw an exception, instead, it wraps it in
-a `StoreResponse.Error` type which allows `Flow` to continue so that it can still receive updates
-that might be triggered by either changes in your data source or subsequent fetch operations.
+#### Updating
+
+##### Request
```kotlin
-viewModelScope.launch {
- store.stream(StoreRequest.cached(key = key, refresh = true)).collect { response ->
- when (response) {
- is StoreResponse.Loading -> showLoadingSpinner()
- is StoreResponse.Data -> {
- if (response.origin == ResponseOrigin.Fetcher) hideLoadingSpinner()
- updateUI(response.value)
- }
- is StoreResponse.Error -> {
- if (response.origin == ResponseOrigin.Fetcher) hideLoadingSpinner()
- showError(response.error)
- }
- }
- }
-}
+store.write(
+ request = StoreWriteRequest.of(
+ key = key,
+ value = newValue
+ )
+)
```
-For convenience, there are `Store.get(key)` and `Store.fresh(key)` extension functions.
+##### Response
-* `suspend fun Store.get(key: Key): Value`: This method returns a single value for the given key. If
- available, it will be returned from the in memory cache or the sourceOfTruth. An error will be
- thrown if no value is available in either the `cache` or `sourceOfTruth`, and the `fetcher` fails
- to load the data from the network.
-* `suspend fun Store.fresh(key: Key): Value`: This method returns a single value for the given key
- that is obtained by querying the fetcher. An error will be thrown if the `fetcher` fails to load
- the data from the network, regardless of whether any value is available in the `cache`
- or `sourceOfTruth`.
+```text
+1. StoreWriteResponse.Success.Typed(response)
+```
+
+#### Deleting
+
+##### Request
```kotlin
-lifecycleScope.launchWhenStarted {
- val article = store.get(key)
- updateUI(article)
-}
+store.clear(key)
```
-The first time you call to `suspend store.get(key)`, the response will be stored in an in-memory
-cache and in the sourceOfTruth, if provided.
-All subsequent calls to `store.get(key)` with the same `Key` will retrieve the cached version of the
-data, minimizing unnecessary data calls. This prevents your app from fetching fresh data over the
-network (or from another external data source) in situations when doing so would unnecessarily waste
-bandwidth and battery. A great use case is any time your views are recreated after a rotation, they
-will be able to request the cached data from your Store. Having this data available can help you
-avoid the need to retain this in the view layer.
-
-By default, 100 items will be cached in memory for 24 hours. You
-may [pass in your own memory policy to override the default policy](#Configuring-In-memory-Cache).
-
-### Skipping Memory/Disk
-
-Alternatively, you can call `store.fresh(key)` to get a `suspended result` that skips the memory (
-and optional disk cache).
-
-A good use case is overnight background updates use `fresh()` to make sure that calls
-to `store.get()` will not have to hit the network during normal usage. Another good use case
-for `fresh()` is when a user wants to pull to refresh.
-
-Calls to both `fresh()` and `get()` emit one value or throw an error.
-
-### Stream
-
-For real-time updates, you may also call `store.stream()` which returns a `Flow` that emits each
-time a new item is returned from your store. You can think of stream as a way to create reactive
-streams that update when you db or memory cache updates
-
-example calls:
-
-```kotlin
-lifecycleScope.launchWhenStarted {
- store.stream(
- StoreRequest.cached(
- 3,
- refresh = false
- )
- ) //will get cached value followed by any fresh values, refresh will also trigger network call if set to `true` even if the data is available in cache or disk.
- .collect {}
- store.stream(StoreRequest.fresh(3)) //skip cache, go directly to fetcher
- .collect {}
-}
-```
-
-### Inflight Debouncer
-
-To prevent duplicate requests for the same data, Store offers an inflight debouncer. If the same
-request is made as a previous identical request that has not completed, the same response will be
-returned. This is useful for situations when your app needs to make many async calls for the same
-data at startup or when users are obsessively pulling to refresh. As an example, The New York Times
-news app asynchronously calls `ConfigStore.get()` from 12 different places on startup. The first
-call blocks while all others wait for the data to arrive. We have seen a dramatic decrease in the
-app's data usage after implementing this inflight logic.
-
-### Disk as Cache
-
-Stores can enable disk caching by passing a `SourceOfTruth` into the builder. Whenever a new network
-request is made, the Store will first write to the disk cache and then read from the disk cache.
-
-### Disk as Single Source of Truth
-
-Providing `sourceOfTruth` whose `reader` function can return a `Flow` allows you to make
-Store treat your disk as source of truth.
-Any changes made on disk, even if it is not made by Store, will update the active `Store` streams.
-
-This feature, combined with persistence libraries that provide observable
-queries ([Jetpack Room](https://developer.android.com/jetpack/androidx/releases/room)
-, [SQLDelight](https://github.com/cashapp/sqldelight)
-or [Realm](https://realm.io/products/realm-database/))
-allows you to create offline first applications that can be used without an active network
-connection while still providing a great user experience.
-
-```kotlin
-StoreBuilder
- .from(
- fetcher = Fetcher.of { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
- sourceOfTruth = SourceOfTruth.of(
- reader = db.postDao()::loadPosts,
- writer = db.postDao()::insertPosts,
- delete = db.postDao()::clearFeed,
- deleteAll = db.postDao()::clearAllFeeds
- )
- ).build()
-```
-
-Stores don’t care how you’re storing or retrieving your data from disk. As a result, you can use
-Stores with object storage or any database (Realm, SQLite, CouchDB, Firebase etc). Technically,
-there is nothing stopping you from implementing an in-memory cache for the "sourceOfTruth"
-implementation and instead have two levels of in-memory caching--one with inflated and one with
-deflated models, allowing for sharing of the “sourceOfTruth” cache data between stores.
-
-If using SQLite we recommend working
-with [Room](https://developer.android.com/topic/libraries/architecture/room) which returns a `Flow`
-from a query
-
-The above builder is how we recommend working with data on Android. With the above setup you have:
-
-+ Memory caching with TTL & Size policies
-+ Disk caching with simple integration with Room
-+ In-flight request management
-+ Ability to get cached data or bust through your caches (`get()` vs. `fresh()`)
-+ Ability to listen for any new emissions from network (stream)
-+ Structured Concurrency through APIs build on Coroutines and Kotlin Flow
-
-### Configuring in-memory Cache
-
-You can configure in-memory cache with the `MemoryPolicy`:
-
-```kotlin
-StoreBuilder
- .from(
- fetcher = Fetcher.of { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
- sourceOfTruth = SourceOfTruth.of(
- reader = db.postDao()::loadPosts,
- writer = db.postDao()::insertPosts,
- delete = db.postDao()::clearFeed,
- deleteAll = db.postDao()::clearAllFeeds
- )
- ).cachePolicy(
- MemoryPolicy.builder()
- .setMaxSize(10)
- .setExpireAfterAccess(10.minutes) // or setExpireAfterWrite(10.minutes)
- .build()
- ).build()
-```
-
-* `setMaxSize(maxSize: Long)` sets the maximum number of entries to be kept in the cache before
- starting to evict the least recently used items.
-* `setExpireAfterAccess(expireAfterAccess: Duration)` sets the maximum time an entry can live in the
- cache since the last access, where "access" means reading the cache, adding a new cache entry, and
- replacing an existing entry with a new one. This duration is also known as **time-to-idle (TTI)**.
-* `setExpireAfterWrite(expireAfterWrite: Duration)` sets the maximum time an entry can live in the
- cache since the last write, where "write" means adding a new cache entry and replacing an existing
- entry with a new one. This duration is also known as **time-to-live (TTL)**.
-
-Note that `setExpireAfterAccess` and `setExpireAfterWrite` **cannot** both be set at the same time.
-
-### Clearing store entries
-
-You can delete a specific entry by key from a store, or clear all entries in a store.
-
-#### Store with no sourceOfTruth
-
-```kotlin
-val store = StoreBuilder
- .from(
- fetcher = Fetcher.of { key: String ->
- api.fetchData(key)
- }).build()
-```
-
-The following will clear the entry associated with the key from the in-memory cache:
-
-```kotlin
-store.clear("10")
-```
-
-The following will clear all entries from the in-memory cache:
-
-```kotlin
-store.clearAll()
-```
-
-#### Store with sourceOfTruth
-
-When store has a sourceOfTruth, you'll need to provide the `delete` and `deleteAll` functions
-for `clear(key)` and `clearAll()` to work:
-
-```kotlin
-StoreBuilder
- .from(
- fetcher = Fetcher.of { api.fetchData(key) },
- sourceOfTruth = SourceOfTruth.of(
- reader = dao::loadData,
- writer = dao::writeData,
- delete = dao::clearDataByKey,
- deleteAll = dao::clearAllData
- )
- ).build()
-```
-
-The following will clear the entry associated with the key from both the in-memory cache and the
-sourceOfTruth:
-
-```kotlin
-store.clear("myKey")
-```
-
-The following will clear all entries from both the in-memory cache and the sourceOfTruth:
-
-```kotlin
-store.clearAll()
-```
-
-## License
+### License
```text
Copyright (c) 2022 Mobile Native Foundation.
diff --git a/cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/CacheBuilder.kt b/cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/CacheBuilder.kt
index f0eb400..a40afe3 100644
--- a/cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/CacheBuilder.kt
+++ b/cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/CacheBuilder.kt
@@ -2,7 +2,7 @@ package org.mobilenativefoundation.store.cache5
import kotlin.time.Duration
-class CacheBuilder {
+class CacheBuilder {
internal var concurrencyLevel = 4
private set
internal val initialCapacity = 16
@@ -14,41 +14,41 @@ class CacheBuilder {
private set
internal var expireAfterWrite: Duration = Duration.INFINITE
private set
- internal var weigher: Weigher? = null
+ internal var weigher: Weigher? = null
private set
internal var ticker: Ticker? = null
private set
- fun concurrencyLevel(producer: () -> Int): CacheBuilder = apply {
+ fun concurrencyLevel(producer: () -> Int): CacheBuilder = apply {
concurrencyLevel = producer.invoke()
}
- fun maximumSize(maximumSize: Long): CacheBuilder = apply {
+ fun maximumSize(maximumSize: Long): CacheBuilder = apply {
if (maximumSize < 0) {
throw IllegalArgumentException("Maximum size must be non-negative.")
}
this.maximumSize = maximumSize
}
- fun expireAfterAccess(duration: Duration): CacheBuilder = apply {
+ fun expireAfterAccess(duration: Duration): CacheBuilder = apply {
if (duration.isNegative()) {
throw IllegalArgumentException("Duration must be non-negative.")
}
expireAfterAccess = duration
}
- fun expireAfterWrite(duration: Duration): CacheBuilder = apply {
+ fun expireAfterWrite(duration: Duration): CacheBuilder = apply {
if (duration.isNegative()) {
throw IllegalArgumentException("Duration must be non-negative.")
}
expireAfterWrite = duration
}
- fun ticker(ticker: Ticker): CacheBuilder = apply {
+ fun ticker(ticker: Ticker): CacheBuilder = apply {
this.ticker = ticker
}
- fun weigher(maximumWeight: Long, weigher: Weigher): CacheBuilder = apply {
+ fun weigher(maximumWeight: Long, weigher: Weigher): CacheBuilder = apply {
if (maximumWeight < 0) {
throw IllegalArgumentException("Maximum weight must be non-negative.")
}
@@ -57,7 +57,7 @@ class CacheBuilder {
this.weigher = weigher
}
- fun build(): Cache {
+ fun build(): Cache {
if (maximumSize != -1L && weigher != null) {
throw IllegalStateException("Maximum size cannot be combined with weigher.")
}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Converter.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Converter.kt
new file mode 100644
index 0000000..3128621
--- /dev/null
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Converter.kt
@@ -0,0 +1,47 @@
+package org.mobilenativefoundation.store.store5
+
+interface Converter {
+ fun fromNetworkToOutput(network: Network): Output?
+ fun fromOutputToLocal(common: Output): Local?
+ fun fromLocalToOutput(sourceOfTruth: Local): Output?
+
+ class Builder {
+
+ private var fromOutputToLocal: ((value: Output) -> Local)? = null
+ private var fromNetworkToOutput: ((value: Network) -> Output)? = null
+ private var fromLocalToOutput: ((value: Local) -> Output)? = null
+
+ fun build(): Converter =
+ RealConverter(fromOutputToLocal, fromNetworkToOutput, fromLocalToOutput)
+
+ fun fromOutputToLocal(converter: (value: Output) -> Local): Builder {
+ fromOutputToLocal = converter
+ return this
+ }
+
+ fun fromLocalToOutput(converter: (value: Local) -> Output): Builder {
+ fromLocalToOutput = converter
+ return this
+ }
+
+ fun fromNetworkToOutput(converter: (value: Network) -> Output): Builder {
+ fromNetworkToOutput = converter
+ return this
+ }
+ }
+}
+
+private class RealConverter(
+ private val fromOutputToLocal: ((value: Output) -> Local)?,
+ private val fromNetworkToOutput: ((value: Network) -> Output)?,
+ private val fromLocalToOutput: ((value: Local) -> Output)?,
+) : Converter {
+ override fun fromNetworkToOutput(network: Network): Output? =
+ fromNetworkToOutput?.invoke(network)
+
+ override fun fromOutputToLocal(common: Output): Local? =
+ fromOutputToLocal?.invoke(common)
+
+ override fun fromLocalToOutput(sourceOfTruth: Local): Output? =
+ fromLocalToOutput?.invoke(sourceOfTruth)
+}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Fetcher.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Fetcher.kt
index b3a0b76..4a4f483 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Fetcher.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Fetcher.kt
@@ -19,11 +19,11 @@ import org.mobilenativefoundation.store.store5.Fetcher.Companion.ofResult
* See [ofFlow], [of] for easily translating to [FetcherResult] (and
* automatically transforming exceptions into [FetcherResult.Error].
*/
-interface Fetcher {
+interface Fetcher {
/**
* Returns a flow of the item represented by the given [key].
*/
- operator fun invoke(key: Key): Flow>
+ operator fun invoke(key: Key): Flow>
companion object {
/**
@@ -37,9 +37,9 @@ interface Fetcher {
*
* @param flowFactory a factory for a [Flow]ing source of network records.
*/
- fun ofResultFlow(
- flowFactory: (Key) -> Flow>
- ): Fetcher = FactoryFetcher(factory = flowFactory)
+ fun ofResultFlow(
+ flowFactory: (Key) -> Flow>
+ ): Fetcher = FactoryFetcher(factory = flowFactory)
/**
* "Creates" a [Fetcher] from a non-[Flow] source.
@@ -52,9 +52,9 @@ interface Fetcher {
*
* @param fetch a source of network records.
*/
- fun ofResult(
- fetch: suspend (Key) -> FetcherResult
- ): Fetcher = ofResultFlow(fetch.asFlow())
+ fun ofResult(
+ fetch: suspend (Key) -> FetcherResult
+ ): Fetcher = ofResultFlow(fetch.asFlow())
/**
* "Creates" a [Fetcher] from a [flowFactory] and translate the results to a [FetcherResult].
@@ -68,11 +68,11 @@ interface Fetcher {
*
* @param flowFactory a factory for a [Flow]ing source of network records.
*/
- fun ofFlow(
- flowFactory: (Key) -> Flow
- ): Fetcher = FactoryFetcher { key: Key ->
+ fun ofFlow(
+ flowFactory: (Key) -> Flow
+ ): Fetcher = FactoryFetcher { key: Key ->
flowFactory(key)
- .map> { FetcherResult.Data(it) }
+ .map> { FetcherResult.Data(it) }
.catch { throwable: Throwable -> emit(FetcherResult.Error.Exception(throwable)) }
}
@@ -87,19 +87,19 @@ interface Fetcher {
*
* @param fetch a source of network records.
*/
- fun of(fetch: suspend (key: Key) -> NetworkRepresentation): Fetcher =
+ fun of(fetch: suspend (key: Key) -> Network): Fetcher =
ofFlow(fetch.asFlow())
- private fun (suspend (key: Key) -> NetworkRepresentation).asFlow() = { key: Key ->
+ private fun (suspend (key: Key) -> Network).asFlow() = { key: Key ->
flow {
emit(invoke(key))
}
}
- private class FactoryFetcher(
- private val factory: (Key) -> Flow>
- ) : Fetcher {
- override fun invoke(key: Key): Flow> = factory(key)
+ private class FactoryFetcher(
+ private val factory: (Key) -> Flow>
+ ) : Fetcher {
+ override fun invoke(key: Key): Flow> = factory(key)
}
}
}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/FetcherResult.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/FetcherResult.kt
index bb24f79..d256bae 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/FetcherResult.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/FetcherResult.kt
@@ -1,7 +1,7 @@
package org.mobilenativefoundation.store.store5
-sealed class FetcherResult {
- data class Data(val value: NetworkRepresentation) : FetcherResult()
+sealed class FetcherResult {
+ data class Data(val value: Network) : FetcherResult()
sealed class Error : FetcherResult() {
data class Exception(val error: Throwable) : Error()
data class Message(val message: String) : Error()
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/ItemValidator.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/ItemValidator.kt
deleted file mode 100644
index 72324b7..0000000
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/ItemValidator.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.mobilenativefoundation.store.store5
-
-import org.mobilenativefoundation.store.store5.impl.RealItemValidator
-
-/**
- * Enables custom validation of [Store] items.
- * @see [StoreReadRequest]
- */
-interface ItemValidator {
- /**
- * Determines whether a [Store] item is valid.
- * If invalid, [MutableStore] will get the latest network value using [Fetcher].
- * [MutableStore] will not validate network responses.
- */
- suspend fun isValid(item: CommonRepresentation): Boolean
-
- companion object {
- fun by(
- validator: suspend (item: CommonRepresentation) -> Boolean
- ): ItemValidator = RealItemValidator(validator)
- }
-}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/MutableStore.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/MutableStore.kt
index 621a1a3..5e17c1f 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/MutableStore.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/MutableStore.kt
@@ -1,8 +1,8 @@
package org.mobilenativefoundation.store.store5
-interface MutableStore :
- Read.StreamWithConflictResolution,
- Write,
- Write.Stream,
+interface MutableStore :
+ Read.StreamWithConflictResolution,
+ Write,
+ Write.Stream,
Clear.Key,
Clear
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnFetcherCompletion.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnFetcherCompletion.kt
index 4d51439..1aac9e4 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnFetcherCompletion.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnFetcherCompletion.kt
@@ -1,6 +1,6 @@
package org.mobilenativefoundation.store.store5
-data class OnFetcherCompletion(
- val onSuccess: (FetcherResult.Data) -> Unit,
+data class OnFetcherCompletion(
+ val onSuccess: (FetcherResult.Data) -> Unit,
val onFailure: (FetcherResult.Error) -> Unit
)
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnUpdaterCompletion.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnUpdaterCompletion.kt
index ad6706b..feae165 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnUpdaterCompletion.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/OnUpdaterCompletion.kt
@@ -1,6 +1,6 @@
package org.mobilenativefoundation.store.store5
-data class OnUpdaterCompletion(
+data class OnUpdaterCompletion(
val onSuccess: (UpdaterResult.Success) -> Unit,
val onFailure: (UpdaterResult.Error) -> Unit
)
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Read.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Read.kt
index 28ed15f..e5c142b 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Read.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Read.kt
@@ -3,15 +3,15 @@ package org.mobilenativefoundation.store.store5
import kotlinx.coroutines.flow.Flow
interface Read {
- interface Stream {
+ interface Stream {
/**
* Return a flow for the given key
* @param request - see [StoreReadRequest] for configurations
*/
- fun stream(request: StoreReadRequest): Flow>
+ fun stream(request: StoreReadRequest): Flow>
}
- interface StreamWithConflictResolution {
- fun stream(request: StoreReadRequest): Flow>
+ interface StreamWithConflictResolution {
+ fun stream(request: StoreReadRequest): Flow>
}
}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/SourceOfTruth.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/SourceOfTruth.kt
index 36e889c..e1c1c6a 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/SourceOfTruth.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/SourceOfTruth.kt
@@ -46,14 +46,14 @@ import kotlin.jvm.JvmName
* transform them to another type when placing them in local storage.
*
*/
-interface SourceOfTruth {
+interface SourceOfTruth {
/**
* Used by [Store] to read records from the source of truth.
*
* @param key The key to read for.
*/
- fun reader(key: Key): Flow
+ fun reader(key: Key): Flow
/**
* Used by [Store] to write records **coming in from the fetcher (network)** to the source of
@@ -66,7 +66,7 @@ interface SourceOfTruth {
*
* @param key The key to update for.
*/
- suspend fun write(key: Key, value: SourceOfTruthRepresentation)
+ suspend fun write(key: Key, value: Local)
/**
* Used by [Store] to delete records in the source of truth for the given key.
@@ -90,12 +90,12 @@ interface SourceOfTruth {
* @param delete function for deleting records in the source of truth for the given key
* @param deleteAll function for deleting all records in the source of truth
*/
- fun of(
- nonFlowReader: suspend (Key) -> SourceOfTruthRepresentation?,
- writer: suspend (Key, SourceOfTruthRepresentation) -> Unit,
+ fun of(
+ nonFlowReader: suspend (Key) -> Local?,
+ writer: suspend (Key, Local) -> Unit,
delete: (suspend (Key) -> Unit)? = null,
deleteAll: (suspend () -> Unit)? = null
- ): SourceOfTruth = PersistentNonFlowingSourceOfTruth(
+ ): SourceOfTruth = PersistentNonFlowingSourceOfTruth(
realReader = nonFlowReader,
realWriter = writer,
realDelete = delete,
@@ -112,12 +112,12 @@ interface SourceOfTruth {
* @param deleteAll function for deleting all records in the source of truth
*/
@JvmName("ofFlow")
- fun of(
- reader: (Key) -> Flow,
- writer: suspend (Key, SourceOfTruthRepresentation) -> Unit,
+ fun of(
+ reader: (Key) -> Flow,
+ writer: suspend (Key, Local) -> Unit,
delete: (suspend (Key) -> Unit)? = null,
deleteAll: (suspend () -> Unit)? = null
- ): SourceOfTruth = PersistentSourceOfTruth(
+ ): SourceOfTruth = PersistentSourceOfTruth(
realReader = reader,
realWriter = writer,
realDelete = delete,
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Store.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Store.kt
index e1bf5ca..6fc93df 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Store.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/Store.kt
@@ -31,7 +31,7 @@ package org.mobilenativefoundation.store.store5
* }
*
*/
-interface Store :
- Read.Stream,
+interface Store :
+ Read.Stream,
Clear.Key,
Clear.All
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreBuilder.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreBuilder.kt
index 0c73069..877502c 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreBuilder.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreBuilder.kt
@@ -22,37 +22,37 @@ import org.mobilenativefoundation.store.store5.impl.storeBuilderFromFetcherAndSo
/**
* Main entry point for creating a [Store].
*/
-interface StoreBuilder {
- fun build(): Store
+interface StoreBuilder {
+ fun build(): Store
- fun build(
- updater: Updater,
+ fun build(
+ updater: Updater,
bookkeeper: Bookkeeper
- ): MutableStore
+ ): MutableStore
/**
- * A store multicasts same [CommonRepresentation] value to many consumers (Similar to RxJava.share()), by default
+ * A store multicasts same [Output] value to many consumers (Similar to RxJava.share()), by default
* [Store] will open a global scope for management of shared responses, if instead you'd like to control
* the scope that sharing/multicasting happens in you can pass a @param [scope]
*
* @param scope - scope to use for sharing
*/
- fun scope(scope: CoroutineScope): StoreBuilder
+ fun scope(scope: CoroutineScope): StoreBuilder
/**
* controls eviction policy for a store cache, use [MemoryPolicy.MemoryPolicyBuilder] to configure a TTL
* or size based eviction
* Example: MemoryPolicy.builder().setExpireAfterWrite(10.seconds).build()
*/
- fun cachePolicy(memoryPolicy: MemoryPolicy?): StoreBuilder
+ fun cachePolicy(memoryPolicy: MemoryPolicy?): StoreBuilder
/**
* by default a Store caches in memory with a default policy of max items = 100
*/
- fun disableCache(): StoreBuilder
+ fun disableCache(): StoreBuilder
- fun converter(converter: StoreConverter):
- StoreBuilder
+ fun converter(converter: Converter):
+ StoreBuilder
companion object {
@@ -61,9 +61,9 @@ interface StoreBuilder from(
- fetcher: Fetcher,
- ): StoreBuilder = storeBuilderFromFetcher(fetcher = fetcher)
+ fun from(
+ fetcher: Fetcher,
+ ): StoreBuilder = storeBuilderFromFetcher(fetcher = fetcher)
/**
* Creates a new [StoreBuilder] from a [Fetcher] and a [SourceOfTruth].
@@ -71,10 +71,10 @@ interface StoreBuilder from(
- fetcher: Fetcher,
- sourceOfTruth: SourceOfTruth
- ): StoreBuilder =
+ fun from(
+ fetcher: Fetcher,
+ sourceOfTruth: SourceOfTruth
+ ): StoreBuilder =
storeBuilderFromFetcherAndSourceOfTruth(fetcher = fetcher, sourceOfTruth = sourceOfTruth)
}
}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreConverter.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreConverter.kt
deleted file mode 100644
index 0ae216b..0000000
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreConverter.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.mobilenativefoundation.store.store5
-
-import org.mobilenativefoundation.store.store5.internal.definition.Converter
-
-interface StoreConverter {
- fun fromNetworkRepresentationToCommonRepresentation(networkRepresentation: NetworkRepresentation): CommonRepresentation?
- fun fromCommonRepresentationToSourceOfTruthRepresentation(commonRepresentation: CommonRepresentation): SourceOfTruthRepresentation?
- fun fromSourceOfTruthRepresentationToCommonRepresentation(sourceOfTruthRepresentation: SourceOfTruthRepresentation): CommonRepresentation?
-
- class Builder {
-
- private var fromCommonToSourceOfTruth: Converter? = null
- private var fromNetworkToCommon: Converter? = null
- private var fromSourceOfTruthToCommon: Converter? = null
-
- fun build(): StoreConverter =
- RealStoreConverter(fromCommonToSourceOfTruth, fromNetworkToCommon, fromSourceOfTruthToCommon)
-
- fun fromCommonToSourceOfTruth(converter: Converter): Builder {
- fromCommonToSourceOfTruth = converter
- return this
- }
-
- fun fromSourceOfTruthToCommon(converter: Converter): Builder {
- fromSourceOfTruthToCommon = converter
- return this
- }
-
- fun fromNetworkToCommon(converter: Converter): Builder {
- fromNetworkToCommon = converter
- return this
- }
- }
-}
-
-private class RealStoreConverter(
- private val fromCommonToSourceOfTruth: Converter?,
- private val fromNetworkToCommon: Converter?,
- private val fromSourceOfTruthToCommon: Converter?
-) : StoreConverter {
- override fun fromNetworkRepresentationToCommonRepresentation(networkRepresentation: NetworkRepresentation): CommonRepresentation? =
- fromNetworkToCommon?.invoke(networkRepresentation)
-
- override fun fromCommonRepresentationToSourceOfTruthRepresentation(commonRepresentation: CommonRepresentation): SourceOfTruthRepresentation? =
- fromCommonToSourceOfTruth?.invoke(commonRepresentation)
-
- override fun fromSourceOfTruthRepresentationToCommonRepresentation(sourceOfTruthRepresentation: SourceOfTruthRepresentation): CommonRepresentation? =
- fromSourceOfTruthToCommon?.invoke(sourceOfTruthRepresentation)
-}
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt
index eb011a3..572c6ec 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/StoreReadResponse.kt
@@ -22,7 +22,7 @@ package org.mobilenativefoundation.store.store5
* class to represent each response. This allows the flow to keep running even if an error happens
* so that if there is an observable single source of truth, application can keep observing it.
*/
-sealed class StoreReadResponse {
+sealed class StoreReadResponse {
/**
* Represents the source of the Response.
*/
@@ -36,8 +36,8 @@ sealed class StoreReadResponse {
/**
* Data dispatched by [Store]
*/
- data class Data(val value: CommonRepresentation, override val origin: StoreReadResponseOrigin) :
- StoreReadResponse()
+ data class Data