You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
val value:String?= ksafe.getDirect("key", "default", encrypted =false)
217
+
// value == null ✓
218
+
219
+
// Nullable fields in serializable classes
220
+
@Serializable
221
+
data classUserProfile(
222
+
valid:Int,
223
+
valnickname:String?, // Can be null
224
+
valbio:String?// Can be null
225
+
)
226
+
227
+
val profile =UserProfile(1, null, "Hello!")
228
+
ksafe.put("profile", profile, encrypted =true)
229
+
```
230
+
200
231
#### Suspend API (non‑blocking)
201
232
202
233
```Kotlin
@@ -218,6 +249,13 @@ var clicks by ksafe.mutableStateOf(0) // encrypted backing storage
218
249
actionButton { clicks++ }
219
250
```
220
251
252
+
#### Jetpack Compose ♥ KSafe (optional module)
253
+
as already mentioned above, Recomposition‑proof and survives process death with zero boilerplate.
254
+
```Kotlin
255
+
var clicks by ksafe.mutableStateOf(0) // encrypted backing storage
256
+
actionButton { clicks++ }
257
+
```
258
+
221
259
#### Deleting data
222
260
```Kotlin
223
261
ksafe.delete("profile") // suspend (non‑blocking)
@@ -312,6 +350,78 @@ class CounterViewModel(ksafe: KSafe) : ViewModel() {
312
350
313
351
***
314
352
353
+
## Architecture: Hybrid "Hot Cache" 🚀
354
+
355
+
KSafe 1.2.0 introduces a completely rewritten core architecture focusing on zero-latency UI performance.
356
+
357
+
### How It Works
358
+
359
+
**Before (v1.1.x):** Every `getDirect()` call triggered a blocking disk read and decryption on the calling thread. This could cause frame drops in scrollable environments.
360
+
361
+
**Now (v1.2.0):** Data is preloaded asynchronously immediately upon initialization. `getDirect()` now performs an **Atomic Memory Lookup (O(1))**, returning instantly.
362
+
363
+
**Safety:** If data is accessed before the preload finishes (Cold Start), the library automatically falls back to a blocking read to ensure you never receive incorrect default values.
364
+
365
+
### Optimistic Updates
366
+
367
+
`putDirect()` now updates the in-memory cache **immediately**, allowing your UI to reflect changes instantly while the disk encryption and write happen safely in the background.
368
+
369
+
***
370
+
371
+
## Memory Security Policy 🔒
372
+
373
+
You can now choose the trade-off between maximum performance and maximum security regarding data resident in RAM.
374
+
375
+
```Kotlin
376
+
val ksafe =KSafe(
377
+
fileName ="secrets",
378
+
memoryPolicy =KSafeMemoryPolicy.ENCRYPTED// (Default) or PLAIN_TEXT
379
+
)
380
+
```
381
+
382
+
### Policy Options
383
+
384
+
| Policy | Best For | Behavior | Performance |
385
+
|--------|----------|----------|-------------|
386
+
|`ENCRYPTED` (Default) | Tokens, passwords, sensitive data | Stores raw ciphertext in RAM. Decrypts on-demand every time you ask for data, then discards the plaintext immediately. | Slightly higher CPU per read |
387
+
|`PLAIN_TEXT`| User settings, themes, preferences | Decrypts once on load, stores plain values in RAM. | Instant reads, zero CPU overhead per call |
388
+
389
+
Both policies encrypt data on disk. The difference is how data is handled in memory:
390
+
-**ENCRYPTED:** Maximum security against memory dump attacks
391
+
-**PLAIN_TEXT:** Maximum performance for frequently accessed data
392
+
393
+
### Lazy Loading
394
+
395
+
By default, KSafe eagerly preloads data on initialization. If you want to defer loading until first access:
396
+
397
+
```Kotlin
398
+
val archive =KSafe(
399
+
fileName ="archive",
400
+
lazyLoad =true// Skip preload, load on first request
401
+
)
402
+
```
403
+
404
+
### Constructor Parameters
405
+
406
+
```Kotlin
407
+
// Android
408
+
KSafe(
409
+
context:Context,
410
+
fileName:String?=null, // Optional namespace
411
+
lazyLoad:Boolean=false, // Eager (false) or lazy (true) loading
@@ -327,6 +437,11 @@ class CounterViewModel(ksafe: KSafe) : ViewModel() {
327
437
* Not included in iCloud/iTunes backups
328
438
* Automatic cleanup of orphaned keys on first app use after reinstall
329
439
440
+
#### JVM/Desktop
441
+
* AES-256-GCM encryption via standard javax.crypto
442
+
* Keys stored in user home directory with restricted permissions
443
+
* Suitable for desktop applications and server-side use
444
+
330
445
### Error Handling
331
446
If decryption fails (e.g., corrupted data or missing key), KSafe gracefully returns the default value, ensuring your app continues to function.
332
447
@@ -351,7 +466,8 @@ On iOS, KSafe uses a smart detection system:
351
466
352
467
***iOS:** Keychain access requires device to be unlocked
353
468
***Android:** Some devices may not have hardware-backed keystore
354
-
***Both:** Encrypted data is lost if encryption keys are deleted (by design for security)
469
+
***JVM:** No hardware security module; relies on file system permissions
470
+
***All Platforms:** Encrypted data is lost if encryption keys are deleted (by design for security)
355
471
356
472
***
357
473
@@ -369,6 +485,9 @@ KSafe includes comprehensive tests for all platforms. Here are the Gradle comman
369
485
# Run common tests only
370
486
./gradlew :ksafe:commonTest
371
487
488
+
# Run JVM tests
489
+
./gradlew :ksafe:jvmTest
490
+
372
491
# Run Android unit tests (Note: May fail in Robolectric due to KeyStore limitations)
373
492
./gradlew :ksafe:testDebugUnitTest
374
493
@@ -454,12 +573,33 @@ xcrun devicectl device process launch \
454
573
The iOS test app demonstrates:
455
574
- Creating a KSafe instance with a custom file name
456
575
- Observing value changes through Flow simulation (via polling)
457
-
- For production apps, consider using [SKIE](https://skie.touchlab.co/) or [KMP-NativeCoroutines](https://github.com/rickclephas/KMP-NativeCoroutines) for easier Flow consumption from iOS
576
+
- For production apps, consider using [SKIE](https://skie.touchlab.co/) or [KMP-NativeCoroutines](https://github.com/rickclephas/KMP-NativeCoroutines) for easier Flow consumption from iOS
458
577
- Using `putDirect` to immediately update values
459
578
- Real-time UI updates responding to value changes
460
579
461
580
***
462
581
582
+
## Migration from v1.1.x
583
+
584
+
### Binary Compatibility
585
+
The public API surface (`get`, `put`, `getDirect`, `putDirect`) remains backward compatible.
586
+
587
+
### Behavior Changes
588
+
-**Initialization is now eager by default.** If you relied on KSafe doing absolutely nothing until the first call, pass `lazyLoad = true`.
589
+
-**Nullable values now work correctly.** No code changes needed, but you can now safely store `null` values.
590
+
591
+
### Compose Module Import Fix
592
+
If upgrading from early 1.2.0 alphas, update your imports:
0 commit comments