Skip to content

Commit

Permalink
Added Jetpack DataStore (#17)
Browse files Browse the repository at this point in the history
- Added Jetpack DataStore support
- Refactored some general configurations
  • Loading branch information
kotlitecture authored Jul 20, 2024
1 parent 47062c3 commit b0baa7d
Show file tree
Hide file tree
Showing 78 changed files with 588 additions and 237 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Compose Multiplatform Configurable Project Template
# Compose Multiplatform Fully Configurable Project Template

## Supported Platforms

Expand Down Expand Up @@ -173,6 +173,7 @@ The generated project will include a similar table in its README.MD file, but wi
| Dataflow | SQLDelight | [Link](docs/Dataflow/SQLDelight/overview.md) | - | [Link](docs/Dataflow/SQLDelight/usage.md) |
| Dataflow | SQLite (Room) | [Link](docs/Dataflow/SQLite%20%28Room%29/overview.md) | - | [Link](docs/Dataflow/SQLite%20%28Room%29/usage.md) |
| Dataflow | Multiplatform Settings | [Link](docs/Dataflow/Multiplatform%20Settings/overview.md) | - | [Link](docs/Dataflow/Multiplatform%20Settings/usage.md) |
| Dataflow | DataStore | [Link](docs/Dataflow/DataStore/overview.md) | - | [Link](docs/Dataflow/DataStore/usage.md) |
| Dataflow | Basic In-Memory Cache API | [Link](docs/Dataflow/Basic%20In-Memory%20Cache%20API/overview.md) | - | [Link](docs/Dataflow/Basic%20In-Memory%20Cache%20API/usage.md) |
| Dataflow | Ktor Client | [Link](docs/Dataflow/Ktor%20Client/overview.md) | - | [Link](docs/Dataflow/Ktor%20Client/usage.md) |
| Dataflow | Cash App Paging Library | [Link](docs/Dataflow/Cash%20App%20Paging%20Library/overview.md) | - | [Link](docs/Dataflow/Cash%20App%20Paging%20Library/usage.md) |
Expand Down
8 changes: 8 additions & 0 deletions docs/Dataflow/DataStore/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# About

The DataStore library stores data asynchronously, consistently, and transactionally, overcoming some of the drawbacks of SharedPreferences.

# Links

- [Official Site](https://developer.android.com/kotlin/multiplatform/datastore)
- [Integration Guide](https://developer.android.com/kotlin/multiplatform/datastore#creating-datastore)
37 changes: 37 additions & 0 deletions docs/Dataflow/DataStore/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Usage

## Overview

The API can be accessed through:
- `shared.data.source.keyvalue.KeyValueSource` - base class at the core module level.
- `shared.data.source.keyvalue.DataStoreSource` - implementation of the base class.

**KeyValueSource** provides the following methods:

- `read(key: String): T?` - Reads data of type [T] associated with the given [key].
- `save(key: String, value: T)` - Saves the provided [value] of type [T] with the given [key].
- `read(key: String, serializationStrategy: SerializationStrategy<T>): T?` - Reads the value associated with the specified key.
- `save(key: String, value: T, serializationStrategy: SerializationStrategy<T>): T` - Saves the specified key-value pair.
- `remove(key: String, serializationStrategy: SerializationStrategy<T>): T?` - Removes the value associated with the specified key.
- `clear()` - Clears all key-value pairs.

## Example

Class instance is pre-configured via dependency injection (DI) as a singleton in `app.factory.configureKoin`.

To start using, just inject it to your DI managed class.

```kotlin
class TemplateViewModel @Inject constructor(
private val keyValueSource: KeyValueSource
) : BaseViewModel() {

override fun doBind() {
launchAsync("init settings") {
...
val passcode: String? = keyValueSource.read("name")
...
}
}
}
```
21 changes: 8 additions & 13 deletions docs/Dataflow/Multiplatform Settings/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,27 @@
## Overview

The API can be accessed through:
- `shared.data.source.keyvalue.KeyValueSource` - facade interface at the core module level.
- `app.data.source.keyvalue.AppKeyValueSource` - decorator class at the app level.
- `shared.data.source.keyvalue.KeyValueSource` - base class at the core module level.
- `shared.data.source.keyvalue.SettingsKeyValueSource` - implementation of the base class.

The difference is that the class serves as a **decorator** and can provide extra methods without impacting facade implementations.
**KeyValueSource** provides the following methods:

Facade **KeyValueSource** provides the following methods:

- `save(key: String, value: T, serializationStrategy: SerializationStrategy<T>): T` - Saves the specified key-value pair.
- `read(key: String): T?` - Reads data of type [T] associated with the given [key].
- `save(key: String, value: T)` - Saves the provided [value] of type [T] with the given [key].
- `read(key: String, serializationStrategy: SerializationStrategy<T>): T?` - Reads the value associated with the specified key.
- `save(key: String, value: T, serializationStrategy: SerializationStrategy<T>): T` - Saves the specified key-value pair.
- `remove(key: String, serializationStrategy: SerializationStrategy<T>): T?` - Removes the value associated with the specified key.
- `clear()` - Clears all key-value pairs.

Decorator **AppKeyValueSource** also provides the following methods with default serialization strategy applied:

- `read(key: String): T?` - Reads data of type [T] associated with the given [key].
- `save(key: String, value: T)` - Saves the provided [value] of type [T] with the given [key].

## Example

Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.data.KeyValueSourceModule`.
Class instance is pre-configured via dependency injection (DI) as a singleton in `app.di.data.KeyValueSourceModule`.

To start using, just inject it to your DI managed class.

```kotlin
class TemplateViewModel @Inject constructor(
private val keyValueSource: AppKeyValueSource // KeyValueSource
private val keyValueSource: KeyValueSource
) : BaseViewModel() {

override fun doBind() {
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
kotli = "0.5.3"
kotli = "0.5.4"
kotlin = "1.9.23"
kotlinxCoroutines = "1.8.0"
kotlinxCoroutines = "1.8.1"
logback = "1.5.6"
slf4j = "2.0.13"

Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion processor/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
apply from: "${project.rootDir}/gradle/kotlin/processor.gradle"

group = 'com.kotlitecture.kotli'
version = '0.6.0'
version = '0.7.0'
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object Rules {

// kotlin
const val DIKt = "*/DI*.kt"
const val ConfigureKoinDI = "*/configureKoin.kt"
const val CommonAppSrcDir = "app/src"
const val SharedDesignSrcDir = "shared/design/src"
const val CommonAppMainDir = "${CommonAppSrcDir}/commonMain"
Expand Down Expand Up @@ -91,5 +92,6 @@ object Rules {
const val HttpSource = "*/*HttpSource*.kt"
const val KeyValueSource = "*/*KeyValueSource*.kt"
const val SettingsKeyValueSource = "*/*SettingsKeyValueSource*.kt"
const val DataStoreSource = "*/*DataStoreSource.kt"

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object CommonProvider : BaseFeatureProvider() {
override fun createProcessors(): List<FeatureProcessor> = listOf(
CommonKtorProcessor,
CommonStatelyProcessor,
CommonStatelyCollectionsProcessor,
CommonKspProcessor
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kotli.template.multiplatform.compose.common

import kotli.engine.BaseFeatureProcessor
import kotli.engine.FeatureProcessor
import kotli.engine.TemplateState
import kotli.engine.template.VersionCatalogRules
import kotli.engine.template.rule.CleanupMarkedLine
import kotli.engine.template.rule.RemoveMarkedLine
import kotli.template.multiplatform.compose.Rules

object CommonStatelyCollectionsProcessor : BaseFeatureProcessor() {

override fun getId(): String = "common.stately-collections"
override fun isInternal(): Boolean = true

override fun dependencies(): List<Class<out FeatureProcessor>> = listOf(
CommonStatelyProcessor::class.java
)

override fun doApply(state: TemplateState) {
state.onApplyRules(
Rules.BuildGradleSharedData,
CleanupMarkedLine("{common.stately-collections}")
)
}

override fun doRemove(state: TemplateState) {
state.onApplyRules(
Rules.BuildGradleSharedData,
RemoveMarkedLine("{common.stately-collections}")
)
state.onApplyRules(
VersionCatalogRules(
RemoveMarkedLine("stately-concurrent-collections")
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kotli.engine.template.rule.RemoveFile
import kotli.engine.template.rule.RemoveMarkedLine
import kotli.template.multiplatform.compose.Rules
import kotli.template.multiplatform.compose.Tags
import kotli.template.multiplatform.compose.common.CommonStatelyCollectionsProcessor
import kotli.template.multiplatform.compose.common.CommonStatelyProcessor
import kotli.template.multiplatform.compose.showcases.dataflow.cache.CacheShowcasesProcessor
import kotlin.time.Duration.Companion.hours
Expand All @@ -22,8 +23,8 @@ object BasicCacheProcessor : BaseFeatureProcessor() {
override fun getTags(): List<FeatureTag> = Tags.AllClients
override fun getIntegrationEstimate(state: TemplateState): Long = 8.hours.inWholeMilliseconds
override fun dependencies(): List<Class<out FeatureProcessor>> = listOf(
CommonStatelyCollectionsProcessor::class.java,
CacheShowcasesProcessor::class.java,
CommonStatelyProcessor::class.java
)

override fun doApply(state: TemplateState) {
Expand All @@ -46,11 +47,6 @@ object BasicCacheProcessor : BaseFeatureProcessor() {
Rules.BuildGradleSharedData,
RemoveMarkedLine("{dataflow.cache.basic}")
)
state.onApplyRules(
VersionCatalogRules(
RemoveMarkedLine("stately-concurrent-collections")
)
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ object RoomProcessor : BaseFeatureProcessor() {
Rules.DIKt,
RemoveMarkedLine("RoomSource")
)
state.onApplyRules(
Rules.ConfigureKoinDI,
RemoveMarkedLine("RoomSource")
)

// showcases
state.onApplyRules(
Expand All @@ -96,6 +100,10 @@ object RoomProcessor : BaseFeatureProcessor() {
Rules.AppModuleKt,
RemoveMarkedLine("createRoom")
)
state.onApplyRules(
"*/createRoom*.kt",
RemoveFile()
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ object SqlDelightProcessor : BaseFeatureProcessor() {
override fun getId(): String = ID
override fun getTags(): List<FeatureTag> = Tags.AllClients
override fun getWebUrl(state: TemplateState): String = "https://cashapp.github.io/sqldelight/"
override fun getIntegrationUrl(state: TemplateState): String = "https://cashapp.github.io/sqldelight/2.0.2/multiplatform_sqlite/"
override fun getIntegrationUrl(state: TemplateState): String =
"https://cashapp.github.io/sqldelight/2.0.2/multiplatform_sqlite/"

override fun getIntegrationEstimate(state: TemplateState): Long = 4.hours.inWholeMilliseconds

Expand Down Expand Up @@ -95,6 +96,10 @@ object SqlDelightProcessor : BaseFeatureProcessor() {
Rules.AppModuleKt,
RemoveMarkedLine("SqlDelight")
)
state.onApplyRules(
"*/createSqlDriver.kt",
RemoveFile()
)
}

private fun removePaging(state: TemplateState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ package kotli.template.multiplatform.compose.dataflow.keyvalue

import kotli.engine.FeatureProcessor
import kotli.template.multiplatform.compose.dataflow.BaseDataFlowProvider
import kotli.template.multiplatform.compose.dataflow.config.facade.FacadeConfigProcessor
import kotli.template.multiplatform.compose.dataflow.keyvalue.common.CommonKeyValueProcessor
import kotli.template.multiplatform.compose.dataflow.keyvalue.datastore.DataStoreProcessor
import kotli.template.multiplatform.compose.dataflow.keyvalue.settings.SettingsKeyValueProcessor

object KeyValueProvider : BaseDataFlowProvider() {

override fun getId(): String = "dataflow.keyvalue"
override fun isMultiple(): Boolean = true
override fun isMultiple(): Boolean = false
override fun createProcessors(): List<FeatureProcessor> = listOf(
CommonKeyValueProcessor,
SettingsKeyValueProcessor
SettingsKeyValueProcessor,
DataStoreProcessor
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ object CommonKeyValueProcessor : BaseFeatureProcessor() {

override fun getId(): String = ID
override fun isInternal(): Boolean = true
override fun getIntegrationEstimate(state: TemplateState): Long = 30.minutes.inWholeMilliseconds

override fun dependencies(): List<Class<out FeatureProcessor>> = listOf(
KeyValueShowcasesProcessor::class.java
Expand All @@ -26,10 +25,6 @@ object CommonKeyValueProcessor : BaseFeatureProcessor() {
Rules.KeyValueSource,
RemoveFile()
)
state.onApplyRules(
Rules.DIKt,
RemoveMarkedLine("KeyValueSource")
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package kotli.template.multiplatform.compose.dataflow.keyvalue.datastore

import kotli.engine.BaseFeatureProcessor
import kotli.engine.FeatureProcessor
import kotli.engine.FeatureTag
import kotli.engine.TemplateState
import kotli.engine.template.VersionCatalogRules
import kotli.engine.template.rule.CleanupMarkedLine
import kotli.engine.template.rule.RemoveFile
import kotli.engine.template.rule.RemoveMarkedLine
import kotli.template.multiplatform.compose.Rules
import kotli.template.multiplatform.compose.Tags
import kotli.template.multiplatform.compose.dataflow.keyvalue.common.CommonKeyValueProcessor
import kotli.template.multiplatform.compose.platform.client.MobileAndDesktopProcessor
import kotli.template.multiplatform.compose.platform.client.android.AndroidPlatformProcessor
import kotli.template.multiplatform.compose.platform.client.ios.IOSPlatformProcessor
import kotli.template.multiplatform.compose.platform.client.jvm.JvmPlatformProcessor
import kotlin.time.Duration.Companion.hours

object DataStoreProcessor : BaseFeatureProcessor() {

const val ID = "dataflow.keyvalue.datastore"

override fun getId(): String = ID
override fun getTags(): List<FeatureTag> = Tags.MobileAndDesktop
override fun getWebUrl(state: TemplateState): String =
"https://developer.android.com/kotlin/multiplatform/datastore"

override fun getIntegrationUrl(state: TemplateState): String =
"https://developer.android.com/kotlin/multiplatform/datastore#creating-datastore"

override fun getIntegrationEstimate(state: TemplateState): Long = 2.hours.inWholeMilliseconds

override fun canApply(state: TemplateState): Boolean {
return listOfNotNull(
state.getFeature(AndroidPlatformProcessor.ID),
state.getFeature(IOSPlatformProcessor.ID),
state.getFeature(JvmPlatformProcessor.ID)
).isNotEmpty()
}

override fun dependencies(): List<Class<out FeatureProcessor>> = listOf(
MobileAndDesktopProcessor::class.java,
CommonKeyValueProcessor::class.java,
)

override fun doApply(state: TemplateState) {
state.onApplyRules(
Rules.BuildGradleSharedData,
CleanupMarkedLine("{dataflow.keyvalue.datastore}")
)
}

override fun doRemove(state: TemplateState) {
state.onApplyRules(
Rules.DataStoreSource,
RemoveFile()
)
state.onApplyRules(
Rules.ConfigureKoinDI,
RemoveMarkedLine("DataStoreSource"),
RemoveMarkedLine("KeyValueSource"),
)
state.onApplyRules(
Rules.BuildGradleSharedData,
RemoveMarkedLine("{dataflow.keyvalue.datastore}")
)
state.onApplyRules(
VersionCatalogRules(
RemoveMarkedLine("androidx-datastore")
)
)
state.onApplyRules(
"*/createDataStorePath.kt",
RemoveFile()
)
}

}
Loading

0 comments on commit b0baa7d

Please sign in to comment.