Skip to content

Commit 7d91494

Browse files
authored
Merge pull request #30 from AutSoft/dev
Release 2.0.0
2 parents f36966d + 7c8b1a6 commit 7d91494

File tree

46 files changed

+712
-911
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+712
-911
lines changed

README.md

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Krate
22

3-
[![Build Status](https://app.bitrise.io/app/40d6bd22db4cfda8/status.svg?token=0neqv73n3TXp9F0nNxj_rA&branch=main)](https://app.bitrise.io/app/40d6bd22db4cfda8)
3+
![Build Status](https://github.com/AutSoft/Krate/workflows/Build%20and%20test/badge.svg?branch=main)
44

55
![Krate banner](./docs/krate.png)
66

7-
_Krate_ is a `SharedPreferences` wrapper library that uses delegated properties for convenient access to `SharedPreferences` values.
7+
**Krate** is a `SharedPreferences` wrapper library that uses delegated properties for convenient access to `SharedPreferences` values.
88

99
Here's what its basic usage looks like, extending the provided `SimpleKrate` class:
1010

1111
```kotlin
1212
class UserSettings(context: Context) : SimpleKrate(context) {
1313

14-
var notificationsEnabled by booleanPref("notifications_enabled", false)
15-
var loginCount by intPref("login_count", 0)
16-
var nickname by stringPref("nickname", "")
14+
var notificationsEnabled by booleanPref().withDefault(false)
15+
var loginCount by intPref().withDefault(0)
16+
var nickname by stringPref()
1717

1818
}
1919

@@ -24,37 +24,61 @@ Log.d("LOGIN_COUNT", "Count: ${settings.loginCount}")
2424

2525
# Dependency
2626

27-
You can include _Krate_ in your project from the `mavenCentral()` repository, like so:
27+
Krate is available from `mavenCentral()`. You can add it to your dependencies with the following line:
2828

2929
```groovy
30-
implementation 'hu.autsoft:krate:1.2.0'
30+
implementation 'hu.autsoft:krate:2.0.0'
3131
```
3232

33-
# Optionals vs defaults
33+
# Basics
3434

35-
Each stored property can be declared with or without a default value. Here's how the two options differ:
35+
A Krate property is nullable by default. It will have a `null` value if no value has been set for the property yet, and its current value can be erased from `SharedPreferences` completely by setting it to `null`.
3636

37-
### Optional values:
37+
```kotlin
38+
var username: String? by stringPref()
39+
```
40+
41+
### Default values
3842

39-
A property declared with the one-argument delegate function will have a nullable type. It will have a `null` value if no value has been set for this property yet, and its current value can be erased from `SharedPreferences` completely by setting it to `null`.
43+
You can provide a default value for the property by chaining a `withDefault` call on the delegate function. This will give the property a non-nullable type.
4044

4145
```kotlin
42-
var username: String? by stringPref("username")
46+
var username: String by stringPref().withDefault("admin")
4347
```
4448

45-
### Default values:
49+
Reading from this property will return either the value it was last set to, or the default value if it's never been set.
50+
51+
> Note that there's no way to remove these values from `SharedPreferences` (although you could set it explicitly to the default value).
52+
53+
### Custom keys
54+
55+
By default, the the property will be stored under the key of the property's name in the underlying `SharedPreferences` instance.
4656

47-
A property declared with the two-argument delegate function takes its default value as the second argument, and it will have a non-nullable type. Reading from this property will return either the value it was last set to or the default value. Setting this property will update the value stored in `SharedPreferences`. Note that there's no way to remove these values from `SharedPreferences` (although you could set it explicitly to the default value).
57+
You can change this behaviour by explicitly providing the key as an argument:
4858

4959
```kotlin
50-
var username: String by stringPref("username", defaultValue = "admin")
60+
var username: String? by stringPref(key = "USER_NAME")
5161
```
5262

63+
> Note that if you rely on property names as keys, renaming a Krate property will become a breaking change, and the previously stored value will be lost. This can be avoided by adding an explicit key with the name of the original property.
64+
65+
### Validation
66+
67+
You can add validation rules to your Krate properties by calling `validate` on any of Krate's delegate functions:
68+
69+
```kotlin
70+
var percentage: Int by intPref()
71+
.withDefault(0)
72+
.validate { it in 0..100 }
73+
```
74+
75+
If this validation fails, an `IllegalArgumentException` will be thrown.
76+
5377
# Custom Krate implementations
5478

55-
You can usually get away with extending `SimpleKrate`, as it does allow you to pass in a custom name for the `SharedPreferences` to be used to store your values in its constructor as an optional parameter. (If you pass in no `name` parameter to its constructor, it will default to using the instance returned by `PreferenceManager.getDefaultSharedPreferences(context)`.)
79+
You can usually get away with extending `SimpleKrate`, as it does allow you to pass in a custom name for the `SharedPreferences` to be used to store your values in its constructor as an optional parameter. (If you pass in no `name` parameter to its constructor, it will default to using the instance returned by `PreferenceManager.getDefaultSharedPreferences(context)`.)
5680

57-
However, you can also implement the `Krate` interface directly if you want to manage the `SharedPreferences` instance yourself for whatever reason - all this interface requires is a property that holds a `SharedPreferences` instance. With that, you can use the delegate functions the same way as shown above:
81+
However, you can also implement the `Krate` interface directly if you want to manage the `SharedPreferences` instance yourself for whatever reason - all this interface requires is a property that holds a `SharedPreferences` instance. With that, you can use the delegate functions the same way as shown above:
5882

5983
```kotlin
6084
class ExampleCustomKrate(context: Context) : Krate {
@@ -65,7 +89,7 @@ class ExampleCustomKrate(context: Context) : Krate {
6589
sharedPreferences = context.applicationContext.getSharedPreferences("custom_krate_prefs", Context.MODE_PRIVATE)
6690
}
6791

68-
var exampleBoolean by booleanPref("exampleBoolean", false)
92+
var exampleBoolean by booleanPref().withDefault(false)
6993

7094
}
7195
```
@@ -79,7 +103,7 @@ class MainActivity : AppCompatActivity(), Krate {
79103
getPreferences(Context.MODE_PRIVATE) // Could also fetch a named or default SharedPrefs
80104
}
81105

82-
var username by stringPref("username", "")
106+
var username by stringPref().withDefault("")
83107

84108
}
85109
```
@@ -99,24 +123,11 @@ class EncryptedKrate(applicationContext: Context) : Krate {
99123
sharedPreferences = EncryptedSharedPreferences.create(applicationContext, ...)
100124
}
101125

102-
val myStringValue: String by stringPref("my_string_value", "")
126+
val myStringValue: String by stringPref().withDefault("")
103127
}
104128
```
105129

106-
# Validation
107-
108-
You can add validation rules to your Krate properties by calling `validate` on any of Krate's delegate functions:
109-
110-
```kotlin
111-
var percentage: Int by intPref(
112-
key = "percentage",
113-
defaultValue = 0,
114-
).validate { it in 0..100 }
115-
```
116-
117-
If this validation fails, an `IllegalArgumentException` will be thrown.
118-
119-
# Addons
130+
# Serialization addons
120131

121132
Krate, by default, supports the types that `SharedPreferences` supports. These are `Boolean`, `Float`, `Int`, `Long`, `String` and `Set<String>`. You may of course want to store additional types in Krate.
122133

@@ -132,8 +143,8 @@ The usage of the Krate integration is the same for both setups:
132143

133144
```kotlin
134145
class MoshiKrate(context: Context) : SimpleKrate(context) {
135-
var user: User? by moshiPref("user")
136-
var savedArticles: List<Article>? by moshiPref("articles")
146+
var user: User? by moshiPref()
147+
var savedArticles: List<Article>? by moshiPref()
137148
}
138149
```
139150

@@ -152,7 +163,7 @@ class CustomMoshiKrate(context: Context) : SimpleKrate(context) {
152163
If you only want to use Moshi adapters that you generate via Moshi's [codegen facilities](https://github.com/square/moshi#codegen), you can use the following Krate artifact in your project to make use of these adapters:
153164

154165
```groovy
155-
implementation 'hu.autsoft:krate-moshi-codegen:1.2.0'
166+
implementation 'hu.autsoft:krate-moshi-codegen:2.0.0'
156167
```
157168

158169
This will give you a default `Moshi` instance created by a call to `Moshi.Builder().build()`. This instance will find and use any of the adapters generated by Moshi's codegen automatically.
@@ -162,7 +173,7 @@ This will give you a default `Moshi` instance created by a call to `Moshi.Builde
162173
If you rely on [reflection](https://github.com/square/moshi#reflection) for your Moshi serialization, and therefore need a `KotlinJsonAdapterFactory` included in your `Moshi` instance, use the following Krate Moshi dependency:
163174

164175
```groovy
165-
implementation 'hu.autsoft:krate-moshi-reflect:1.2.0'
176+
implementation 'hu.autsoft:krate-moshi-reflect:2.0.0'
166177
```
167178

168179
The default `Moshi` instance from this dependency will include the aforementioned factory, and be able to serialize any Kotlin class. Note that this approach relies on the `kotlin-reflect` library, which is a large dependency.
@@ -174,15 +185,15 @@ You may choose to use Moshi's codegen for some classes in your project, and seri
174185
The `krate-kotlinx` artifact provides a `kotlinxPref` delegate which can store any arbitrary type, as long as Kotlinx.serializazion can serialize and deserialize it. This addon, like the base library, is available from `mavenCentral()`:
175186

176187
```groovy
177-
implementation 'hu.autsoft:krate-kotlinx:1.2.0'
188+
implementation 'hu.autsoft:krate-kotlinx:2.0.0'
178189
```
179190

180191
Its usage is the same as with any of the base library's delegates:
181192

182193
```kotlin
183194
class KotlinxKrate(context: Context) : SimpleKrate(context) {
184-
var user: User? by kotlinxPref("user")
185-
var savedArticles: List<Article>? by kotlinxPref("articles")
195+
var user: User? by kotlinxPref()
196+
var savedArticles: List<Article>? by kotlinxPref()
186197
}
187198
```
188199

@@ -197,7 +208,7 @@ class CustomKotlinxKrate(context: Context) : SimpleKrate(context) {
197208
}
198209
}
199210

200-
var user: User? by kotlinxPref("user")
211+
var user: User? by kotlinxPref()
201212
}
202213
```
203214

@@ -206,15 +217,15 @@ class CustomKotlinxKrate(context: Context) : SimpleKrate(context) {
206217
The `krate-gson` artifact provides a `gsonPref` delegate which can store any arbitrary type, as long as Gson can serialize and deserialize it. This addon, like the base library, is available from `mavenCentral()`:
207218

208219
```groovy
209-
implementation 'hu.autsoft:krate-gson:1.2.0'
220+
implementation 'hu.autsoft:krate-gson:2.0.0'
210221
```
211222

212223
Its basic usage is the same as with any of the base library's delegates:
213224

214225
```kotlin
215226
class GsonKrate(context: Context) : SimpleKrate(context) {
216-
var user: User? by gsonPref("user")
217-
var savedArticles: List<Article>? by gsonPref("articles")
227+
var user: User? by gsonPref()
228+
var savedArticles: List<Article>? by gsonPref()
218229
}
219230
```
220231

@@ -226,7 +237,7 @@ class CustomGsonKrate(context: Context) : SimpleKrate(context) {
226237
gson = GsonBuilder().create()
227238
}
228239

229-
var user: User? by gsonPref("user")
240+
var user: User? by gsonPref()
230241
}
231242
```
232243

app/build.gradle

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
apply plugin: 'com.android.application'
2-
apply plugin: 'kotlin-android'
1+
plugins {
2+
id 'com.android.application'
3+
id 'kotlin-android'
4+
id 'kotlin-kapt'
5+
id 'org.jetbrains.kotlin.plugin.serialization'
6+
}
37

48
android {
59
compileSdkVersion compile_sdk
@@ -22,14 +26,28 @@ android {
2226
}
2327

2428
dependencies {
29+
30+
// Krate
31+
implementation project(path: ':krate')
32+
implementation project(path: ':krate-gson')
33+
implementation project(path: ':krate-kotlinx')
34+
implementation project(path: ':krate-moshi-codegen')
35+
36+
// Android
2537
implementation 'com.google.android.material:material:1.4.0'
26-
implementation 'androidx.appcompat:appcompat:1.3.1'
27-
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
38+
implementation 'androidx.appcompat:appcompat:1.4.0'
39+
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
40+
41+
// KotlinX
42+
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1'
43+
44+
// Moshi
45+
def moshiVersion = "1.12.0"
46+
implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion"
47+
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
48+
implementation "com.squareup.moshi:moshi-adapters:$moshiVersion"
2849

2950
// Third-party SharedPreferences implementations
3051
implementation "androidx.security:security-crypto:1.1.0-alpha03"
3152
implementation 'com.frybits.harmony:harmony:1.1.2'
32-
33-
implementation project(path: ':krate')
34-
implementation project(path: ':krate-gson')
3553
}

app/src/main/java/hu/autsoft/krateexample/ExampleActivity.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import hu.autsoft.krateexample.databinding.ActivityExampleBinding
66
import hu.autsoft.krateexample.krates.ExampleCustomKrate
77
import hu.autsoft.krateexample.krates.ExampleSettings
88
import hu.autsoft.krateexample.krates.ExampleSimpleKrate
9+
import hu.autsoft.krateexample.models.User
910

1011
class ExampleActivity : AppCompatActivity() {
1112

@@ -47,6 +48,12 @@ class ExampleActivity : AppCompatActivity() {
4748
binding.longPreferenceInput.setText(exampleSettings.exampleLong.toString())
4849
binding.stringPreferenceInput.setText(exampleSettings.exampleString)
4950
binding.stringSetPreferenceInput.setText(exampleSettings.exampleStringSet.joinToString(separator = ", "))
51+
binding.gsonPreferenceFirstInput.setText(exampleSettings.exampleUserGson.firstName)
52+
binding.gsonPreferenceLastInput.setText(exampleSettings.exampleUserGson.lastName)
53+
binding.kotlinxPreferenceFirstInput.setText(exampleSettings.exampleUserKotlinX.firstName)
54+
binding.kotlinxPreferenceLastInput.setText(exampleSettings.exampleUserKotlinX.lastName)
55+
binding.moshiPreferenceFirstInput.setText(exampleSettings.exampleUserMoshi.firstName)
56+
binding.moshiPreferenceLastInput.setText(exampleSettings.exampleUserMoshi.lastName)
5057
}
5158

5259
override fun onPause() {
@@ -59,6 +66,18 @@ class ExampleActivity : AppCompatActivity() {
5966
exampleSettings.exampleString = binding.stringPreferenceInput.text.toString()
6067
exampleSettings.exampleStringSet =
6168
binding.stringSetPreferenceInput.text.toString().split(",").map(String::trim).toSet()
69+
exampleSettings.exampleUserGson = User(
70+
binding.gsonPreferenceFirstInput.text.toString(),
71+
binding.gsonPreferenceLastInput.text.toString(),
72+
)
73+
exampleSettings.exampleUserKotlinX = User(
74+
binding.kotlinxPreferenceFirstInput.text.toString(),
75+
binding.kotlinxPreferenceLastInput.text.toString(),
76+
)
77+
exampleSettings.exampleUserMoshi = User(
78+
binding.moshiPreferenceFirstInput.text.toString(),
79+
binding.moshiPreferenceLastInput.text.toString(),
80+
)
6281
}
6382

6483
}

app/src/main/java/hu/autsoft/krateexample/krates/ExampleCustomKrate.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ import android.content.Context
66
import android.content.SharedPreferences
77
import hu.autsoft.krate.Krate
88
import hu.autsoft.krate.booleanPref
9+
import hu.autsoft.krate.default.withDefault
910
import hu.autsoft.krate.floatPref
11+
import hu.autsoft.krate.gson.gsonPref
1012
import hu.autsoft.krate.intPref
13+
import hu.autsoft.krate.kotlinx.kotlinxPref
1114
import hu.autsoft.krate.longPref
15+
import hu.autsoft.krate.moshi.moshiPref
1216
import hu.autsoft.krate.stringPref
1317
import hu.autsoft.krate.stringSetPref
18+
import hu.autsoft.krateexample.models.User
1419

1520
class ExampleCustomKrate(context: Context) : Krate, ExampleSettings {
1621

@@ -20,11 +25,14 @@ class ExampleCustomKrate(context: Context) : Krate, ExampleSettings {
2025
sharedPreferences = context.applicationContext.getSharedPreferences("custom_krate_prefs", Context.MODE_PRIVATE)
2126
}
2227

23-
override var exampleBoolean by booleanPref("exampleBoolean", false)
24-
override var exampleFloat by floatPref("exampleFloat", 0f)
25-
override var exampleInt by intPref("exampleInt", 0)
26-
override var exampleLong by longPref("exampleLong", 0L)
27-
override var exampleString by stringPref("exampleString", "")
28-
override var exampleStringSet by stringSetPref("exampleStringSet", setOf())
28+
override var exampleBoolean by booleanPref().withDefault(false)
29+
override var exampleFloat by floatPref().withDefault(0f)
30+
override var exampleInt by intPref().withDefault(0)
31+
override var exampleLong by longPref().withDefault(0L)
32+
override var exampleString by stringPref().withDefault("")
33+
override var exampleStringSet by stringSetPref().withDefault(setOf())
34+
override var exampleUserGson by gsonPref<User>().withDefault(User("Gson", "Green"))
35+
override var exampleUserKotlinX by kotlinxPref<User>().withDefault(User("KotlinX", "Klark"))
36+
override var exampleUserMoshi by moshiPref<User>().withDefault(User("Moshi", "Miller"))
2937

3038
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package hu.autsoft.krateexample.krates
22

3+
import hu.autsoft.krateexample.models.User
4+
35
interface ExampleSettings {
46
var exampleBoolean: Boolean
57
var exampleFloat: Float
68
var exampleInt: Int
79
var exampleLong: Long
810
var exampleString: String
911
var exampleStringSet: Set<String>
12+
var exampleUserGson: User
13+
var exampleUserKotlinX: User
14+
var exampleUserMoshi: User
1015
}

0 commit comments

Comments
 (0)