Skip to content

Commit

Permalink
ICodable for custom types
Browse files Browse the repository at this point in the history
  • Loading branch information
HubbleCommand committed Jun 5, 2024
1 parent 3316dc0 commit e545b84
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 10 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@

1. [Introduction](#preftils)
2. [Development](#development)
3. [Adding Dependency](#adding-dependency)
4. [Basic Usage](#basic-usage)
5. [Advanced Usage with ICodable](#advanced-usage-with-icodable)

# Preftils - Kotlin Android Shared Preference Utils

[pkg-url]: https://jitpack.io/#hubblecommand/preftils
Expand Down Expand Up @@ -32,7 +39,7 @@ dependencies {
}
```

## Usage
## Basic Usage
Check the Instrumented test for sample code. Ideally, you can use this library with a Kotlin object like so:
```
object Preferences {
Expand All @@ -53,3 +60,6 @@ with (PreferenceManager.getDefaultSharedPreferences(this).edit()) {
apply()
}
```

## Advanced usage with `ICodable`
Custom types are also supported, as long as they implement the `ICodable` interface. Once you have implemented a way to encode & decode your custom class, you can easily use it with SharedPreferences like with the other primitive types. An example is provided in the Instrumented tests as before.
Original file line number Diff line number Diff line change
@@ -1,40 +1,101 @@
package com.hubble.preftils

import android.content.Context
import android.content.SharedPreferences
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*
import org.junit.BeforeClass

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*
* Test results are in build/reports/androidTests...
*/
@RunWith(AndroidJUnit4::class)
class InstrumentedTest {
object PreferencesTest {
val NUMBER = Preference("number", 1)
}

companion object {
lateinit var preferences: SharedPreferences

@BeforeClass
@JvmStatic
internal fun initialize() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
preferences = appContext.applicationContext.getSharedPreferences("prefs", Context.MODE_PRIVATE)

with (preferences.edit()) {
remove(PreferencesTest.NUMBER.key)
remove(PreferencesCodableTest.CODABLE.key)
apply()
}
}
}

@Test
fun test() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
val prefs = appContext.applicationContext.getSharedPreferences("prefs", Context.MODE_PRIVATE)

val initial = prefs.get(PreferencesTest.NUMBER)
val target = 2
val initial = preferences.get(PreferencesTest.NUMBER)
assertEquals(PreferencesTest.NUMBER.default, initial)

with (prefs.edit()) {
val target = 2
with (preferences.edit()) {
put(PreferencesTest.NUMBER, target)
apply()
}

val set = prefs.get(PreferencesTest.NUMBER)
val set = preferences.get(PreferencesTest.NUMBER)
assertEquals(target, set)
}
}

@Test
fun testIncompatibleType() {
val invalidPreferenceType = Preference("invalid", 20u)
try {
preferences.get(invalidPreferenceType)
fail("Should have thrown")
} catch (_: IllegalArgumentException) {

}
}

class CodableTest(val a: Int, val b: Boolean): ICodable {
override fun encode(): String {
return "$a,$b"
}

override fun decode(string: String): CodableTest {
val split = string.split(",")
return CodableTest(split[0].toInt(), split[1].toBooleanStrict())
}
}

object PreferencesCodableTest {
val CODABLE = Preference("codable", CodableTest(17, false))
}

@Test
fun testCodable() {
val initial = preferences.get(PreferencesCodableTest.CODABLE)
assertEquals(PreferencesCodableTest.CODABLE.default.a, initial.a)
assertEquals(PreferencesCodableTest.CODABLE.default.b, initial.b)

val target = CodableTest(20, true)
with (preferences.edit()) {
put(PreferencesCodableTest.CODABLE, target)
apply()
}

val set = preferences.get(PreferencesCodableTest.CODABLE)
assertEquals(target.a, set.a)
assertEquals(target.b, set.b)
}
}
15 changes: 14 additions & 1 deletion preftils/src/main/java/com/hubble/preftils/PreferenceUtils.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.hubble.preftils


import android.content.SharedPreferences

interface ICodable {
fun encode(): String
@Throws
fun decode(string: String): ICodable
}

data class Preference<T>(val key: String, val default: T)

//TODO see about removing reified for Java interoperability
Expand All @@ -14,6 +19,10 @@ inline fun <reified T> SharedPreferences.get(preference: Preference<T>): T {
is Long -> this.getLong(preference.key, preference.default) as T
is Boolean -> this.getBoolean(preference.key, preference.default) as T
is Float -> this.getFloat(preference.key, preference.default) as T
is ICodable -> {
val str = this.getString(preference.key, null) ?: return preference.default
preference.default.decode(str) as T
}
else -> throw IllegalArgumentException("Unsupported type")
}
}
Expand All @@ -27,5 +36,9 @@ inline fun <reified T> SharedPreferences.Editor.put(preference: Preference<T>, v
is Long -> this.putLong(preference.key, value)
is Boolean -> this.putBoolean(preference.key, value)
is Float -> this.putFloat(preference.key, value)
is ICodable -> {
this.putString(preference.key, value.encode())
}
else -> throw IllegalArgumentException("Unsupported type")
}
}

0 comments on commit e545b84

Please sign in to comment.