This is probably wrong -- but how can I fix it wit...
# getting-started
d
This is probably wrong -- but how can I fix it without using kotlin-reflect?
Copy code
@ExperimentalStdlibApi
inline operator fun <reified T : Any> SharedPreferences.getValue(owner: Any?, property: KProperty<*>): T {
    return when (val type = typeOf<T>()) {
        is Set<*> ->
            getStringSet(property.name.replace('_', '.'), emptySet()) ?: emptySet()

        else -> error("Invalid type")
    } as T
}
j
Wrong in which way?
d
typeOf<T>()
is a
KType
whereas
is
I think only works with actual instances...
someobjinstance is Set<*>
j
Oh sorry I thought you meant wrong as in "the working but wrong approach"
What's your goal here? Are you trying to implement a generic property delegate for all types of preferences?
d
Only certain types... basically String, Set<String> and Map<String, Set<String>> (which is build on delimited result of getStringSet).. and the property name is the name of the preference more or less...
j
I mean it can probably be done with `KType.isSubtypeOf`but that will require kotlin reflect. I wonder if it's really necessary to have it generic instead of have a separate delegate for each independent type. It doesn't seem much more inconvenient to declare
val x by stringPreference()
and
val x by stringSetPreference()
If you're ok with exact matches, you can also use:
Copy code
when (val type = typeOf<T>()) {
    typeOf<Set<String>>() -> TODO()
    else -> TODO()
}
But I'm not sure this is reliable.
d
it has a receiver of SharedPreferences... since I have a few possible implementations of SharedPreferences... so it would be
stringSetPreference(prefs)
... not as nice though? What do you mean by exact matches? And why shouldn't it be reliable? I really only have those 3 types...
Seems to work like a charm:
Copy code
import kotlin.reflect.typeOf

inline fun <reified T : Any> c() = when(val type = typeOf<T>()) {
    typeOf<String>() -> 1
    typeOf<Int>() -> 2
    typeOf<Set<String>>() -> 3
    typeOf<Set<Int>>() -> 4
    else -> error("something else: ${type::class}")
}

val num: Any = 0

fun main() {
   println(c<String>())
   println(c<Int>())
   println(c<Set<String>>())
   println(c<Set<Int>>())
}
// Gives:
// 1
// 2
// 3
// 4
Thanks 😃! It seems to work great (even though it give a warning about when returning Any...)!
Copy code
@ExperimentalStdlibApi
inline operator fun <reified T : Any> SharedPreferences.getValue(owner: Any?, property: KProperty<*>): T {
    val keyName = property.name.replace('_', '.')

    return when (typeOf<T>()) {
        typeOf<Set<String>>() ->
            getStringSet(keyName, emptySet<String>()) ?: emptySet<String>()

       typeOf<String>() ->
           getString(keyName, "")

        else -> error("Invalid type")
    } as T
}
j
What do you mean by exact matches?
I meant no supertypes are allowed for
T
with this approach, so for instance a
CharSequence
property won't match the
String
case. But for your use case it might be enough.
And why shouldn't it be reliable?
This is just a disclaimer because I have not looked at how
KType
's equality works. It may work perfectly fine, I just haven't checked
it has a receiver of SharedPreferences... since I have a few possible implementations of SharedPreferences... so it would be 
stringSetPreference(prefs)
... not as nice though?
Ah I see, so you want to simply delegate to
prefs
like
Map
does. Makes sense. You could also design it to work with the syntax
by prefs.string()
/
by prefs.stringSet()
etc.
d
It seems like
typeOf()
returns a
TypeReference
and has:
Copy code
override fun equals(other: Any?): Boolean =
        other is TypeReference &&
                classifier == other.classifier && arguments == other.arguments && isMarkedNullable == other.isMarkedNullable
But anyways, it seems to work for all my current requirements...
👍 1
r
@dave08 check this. It might help. https://github.com/rayarteagas/EasyPreferences (Disclaimer, It’s old and definitely not the best approach for preferences, specially nowadays)