feat(core/preference): Add Darwin implementation

Yoinked from my college final project
This commit is contained in:
Ahmad Ansori Palembani 2024-08-08 07:48:57 +07:00
parent 97417d175f
commit 93962b2649
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
4 changed files with 227 additions and 2 deletions

View file

@ -8,8 +8,11 @@ plugins {
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
val commonMain by getting {
commonMain {
dependencies {
implementation(projects.i18n)
@ -27,7 +30,7 @@ kotlin {
implementation(libs.jsoup)
}
}
val androidMain by getting {
androidMain {
dependencies {
// Dependency injection
api(libs.injekt.core)
@ -47,6 +50,10 @@ kotlin {
implementation(libs.libarchive)
}
}
iosMain {
dependencies {
}
}
}
}

View file

@ -0,0 +1,161 @@
package eu.kanade.tachiyomi.core.preference
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import platform.Foundation.NSNotification
import platform.Foundation.NSNotificationCenter
import platform.Foundation.NSUserDefaults
import platform.Foundation.NSUserDefaultsDidChangeNotification
sealed class DarwinPreference<T>(
private val userDefaults: NSUserDefaults,
private val key: String,
private val defaultValue: T,
) : Preference<T> {
private fun flowForKey(key: String) = callbackFlow {
val observer = NSNotificationCenter.defaultCenter.addObserverForName(
name = NSUserDefaultsDidChangeNotification,
`object` = userDefaults,
queue = null,
) { _: NSNotification? ->
trySend(key)
}
awaitClose {
NSNotificationCenter.defaultCenter.removeObserver(observer)
}
}
abstract fun read(userDefaults: NSUserDefaults, key: String, defaultValue: T): T
override fun key(): String {
return key
}
override fun get(): T {
return try {
read(userDefaults, key, defaultValue)
} catch (e: ClassCastException) {
Logger.i { "Invalid value for $key; deleting" }
delete()
defaultValue
}
}
override fun set(value: T) {
userDefaults.setObject(value, key)
}
override fun isSet(): Boolean {
return userDefaults.objectForKey(key) != null
}
override fun delete() {
userDefaults.removeObjectForKey(key)
}
override fun defaultValue(): T {
return defaultValue
}
override fun changes(): Flow<T> {
return flowForKey(key)
.onStart { emit("ignition") }
.map { get() }
.conflate()
}
override fun stateIn(scope: CoroutineScope): StateFlow<T> {
return changes().stateIn(scope, SharingStarted.Eagerly, get())
}
class StringPrimitive(
userDefaults: NSUserDefaults,
key: String,
defaultValue: String,
) : DarwinPreference<String>(userDefaults, key, defaultValue) {
override fun read(
userDefaults: NSUserDefaults,
key: String,
defaultValue: String,
): String {
return userDefaults.stringForKey(key) ?: defaultValue
}
}
class LongPrimitive(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Long,
) : DarwinPreference<Long>(userDefaults, key, defaultValue) {
override fun read(userDefaults: NSUserDefaults, key: String, defaultValue: Long): Long {
return userDefaults.objectForKey(key) as? Long ?: defaultValue
}
}
class IntPrimitive(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Int,
) : DarwinPreference<Int>(userDefaults, key, defaultValue) {
override fun read(userDefaults: NSUserDefaults, key: String, defaultValue: Int): Int {
return userDefaults.objectForKey(key) as? Int ?: defaultValue
}
}
class FloatPrimitive(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Float,
) : DarwinPreference<Float>(userDefaults, key, defaultValue) {
override fun read(userDefaults: NSUserDefaults, key: String, defaultValue: Float): Float {
return userDefaults.objectForKey(key) as? Float ?: defaultValue
}
}
class BooleanPrimitive(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Boolean,
) : DarwinPreference<Boolean>(userDefaults, key, defaultValue) {
override fun read(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Boolean,
): Boolean {
return userDefaults.objectForKey(key) as? Boolean ?: defaultValue
}
}
class StringSetPrimitive(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Set<String>,
) : DarwinPreference<Set<String>>(userDefaults, key, defaultValue) {
override fun read(
userDefaults: NSUserDefaults,
key: String,
defaultValue: Set<String>,
): Set<String> {
return userDefaults.stringArrayForKey(key) as? Set<String>? ?: defaultValue
}
}
class Object<T>(
userDefaults: NSUserDefaults,
key: String,
defaultValue: T,
val serializer: (T) -> String,
val deserializer: (String) -> T,
) : DarwinPreference<T>(userDefaults, key, defaultValue) {
override fun read(userDefaults: NSUserDefaults, key: String, defaultValue: T): T {
return try {
userDefaults.stringForKey(key)?.let(deserializer) ?: defaultValue
} catch (e: Exception) {
defaultValue
}
}
}
}

View file

@ -0,0 +1,52 @@
package eu.kanade.tachiyomi.core.preference
import eu.kanade.tachiyomi.core.preference.DarwinPreference.*
import platform.Foundation.NSUserDefaults
class DarwinPreferenceStore(
private val userDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults(),
) : PreferenceStore {
override fun getString(key: String, defaultValue: String): Preference<String> {
return StringPrimitive(userDefaults, key, defaultValue)
}
override fun getLong(key: String, defaultValue: Long): Preference<Long> {
return LongPrimitive(userDefaults, key, defaultValue)
}
override fun getInt(key: String, defaultValue: Int): Preference<Int> {
return IntPrimitive(userDefaults, key, defaultValue)
}
override fun getFloat(key: String, defaultValue: Float): Preference<Float> {
return FloatPrimitive(userDefaults, key, defaultValue)
}
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> {
return BooleanPrimitive(userDefaults, key, defaultValue)
}
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> {
return StringSetPrimitive(userDefaults, key, defaultValue)
}
override fun <T> getObject(
key: String,
defaultValue: T,
serializer: (T) -> String,
deserializer: (String) -> T,
): Preference<T> {
return Object(
userDefaults = userDefaults,
key = key,
defaultValue = defaultValue,
serializer = serializer,
deserializer = deserializer,
)
}
override fun getAll(): Map<String, *> {
return userDefaults.dictionaryRepresentation() as? Map<String, *> ?: emptyMap<String, Any>()
}
}

View file

@ -8,6 +8,9 @@ plugins {
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
applyDefaultHierarchyTemplate()
@ -20,6 +23,8 @@ kotlin {
}
androidMain {
}
iosMain {
}
}
}