Remove duplicate AsyncSettings implementation, clean up

This commit is contained in:
2025-12-25 15:36:46 +03:00
Unverified
parent e6900d3169
commit b57ca01596
21 changed files with 103 additions and 457 deletions
@@ -1,18 +0,0 @@
package ru.fromchat
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.format.char
val DATETIME_FORMAT = LocalDateTime.Format {
day()
char('.')
monthNumber()
char('.')
year()
char(' ')
hour()
char(':')
minute()
}
@@ -60,9 +60,7 @@ data class Message(
val reply_to: Message? = null, val reply_to: Message? = null,
val client_message_id: String? = null, val client_message_id: String? = null,
val reactions: List<ReactionData>? = null val reactions: List<ReactionData>? = null
) { )
val utcTimestamp = "${timestamp}Z"
}
@Serializable @Serializable
data class SendMessageRequest( data class SendMessageRequest(
@@ -35,9 +35,4 @@ data class ReactionUser(
data class TypingUpdateData( data class TypingUpdateData(
val userId: Int, val userId: Int,
val username: String val username: String
)
@Serializable
data class WebSocketAuthMessage(
val token: String
) )
@@ -8,19 +8,75 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import ru.fromchat.ui.Theme import ru.fromchat.ui.Theme
/**
* Server configuration data
*/
data class ServerConfigData(
val serverUrl: String,
val httpsEnabled: Boolean
)
object Settings { object Settings {
private const val MATERIAL_YOU_KEY = "materialYou"
private const val THEME_KEY = "theme"
private const val SERVER_URL_KEY = "server_url"
private const val HTTPS_ENABLED_KEY = "https_enabled"
private val settings = Settings() private val settings = Settings()
private fun runIO(block: suspend CoroutineScope.() -> Unit) { private fun runIO(block: suspend CoroutineScope.() -> Unit) {
CoroutineScope(Dispatchers.IO).launch(block = block) CoroutineScope(Dispatchers.IO).launch(block = block)
} }
private fun serverConfigNotInitialized(): Nothing
= throw IllegalStateException("Server config not initialized")
var materialYou: Boolean var materialYou: Boolean
get() = runBlocking { settings.getBoolean("materialYou", true) } get() = runBlocking { settings.getBoolean(MATERIAL_YOU_KEY, true) }
set(value) = runIO { settings.putBoolean("materialYou", value) } set(value) = runIO { settings.putBoolean(MATERIAL_YOU_KEY, value) }
var theme: Theme var theme: Theme
get() = runBlocking { Theme.entries[settings.getInt("theme", Theme.AsSystem.ordinal)] } get() = runBlocking { Theme.entries[settings.getInt(THEME_KEY, Theme.AsSystem.ordinal)] }
set(value) = runIO { settings.putInt("theme", value.ordinal) } set(value) = runIO { settings.putInt(THEME_KEY, value.ordinal) }
var serverUrl: String
get() = runBlocking {
settings.getString(SERVER_URL_KEY).ifEmpty {
serverConfigNotInitialized()
}
}
set(value) = runIO { settings.putString(SERVER_URL_KEY, value) }
var httpsEnabled: Boolean
get() = runBlocking {
if (settings.contains(HTTPS_ENABLED_KEY))
settings.getBoolean(HTTPS_ENABLED_KEY, true)
else serverConfigNotInitialized()
}
set(value) = runIO { settings.putBoolean(HTTPS_ENABLED_KEY, value) }
val hasServerConfig get() = try {
serverUrl
httpsEnabled
true
} catch (_: IllegalStateException) {
false
}
var serverConfig: ServerConfigData
get() {
if (!hasServerConfig) {
serverUrl = "fromchat.ru"
httpsEnabled = true
return ServerConfigData("fromchat.ru", true)
}
return ServerConfigData(serverUrl, httpsEnabled)
}
set(value) {
serverUrl = value.serverUrl
httpsEnabled = value.httpsEnabled
}
} }
@@ -1,10 +1,10 @@
package ru.fromchat.core.config package ru.fromchat.core.config
import com.pr0gramm3r101.utils.storage.ServerConfigData
import com.pr0gramm3r101.utils.storage.ServerConfigStorage
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import ru.fromchat.core.ServerConfigData
import ru.fromchat.core.Settings
/** /**
* Application configuration * Application configuration
@@ -19,18 +19,15 @@ object Config {
/** /**
* Initialize configuration by loading from storage * Initialize configuration by loading from storage
*/ */
suspend fun initialize() { fun initialize() {
_serverConfig.value = ServerConfigStorage.getConfig() _serverConfig.value = Settings.serverConfig
// Set default values and prevent corruption
ServerConfigStorage.getConfig()
} }
/** /**
* Update server configuration * Update server configuration
*/ */
suspend fun updateServerConfig(config: ServerConfigData) { fun updateServerConfig(config: ServerConfigData) {
ServerConfigStorage.saveConfig(config) Settings.serverConfig = config
_serverConfig.value = config _serverConfig.value = config
} }
@@ -45,9 +42,4 @@ object Config {
*/ */
val webSocketUrl val webSocketUrl
get() = "${if (config.httpsEnabled) "wss" else "ws"}://${config.serverUrl}/api/chat/ws" get() = "${if (config.httpsEnabled) "wss" else "ws"}://${config.serverUrl}/api/chat/ws"
/**
* Checks if server configuration exists
*/
suspend fun hasServerConfig() = ServerConfigStorage.hasConfiguration()
} }
@@ -307,12 +307,13 @@ fun ChatScreen(
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier.fillMaxSize(), // Fill the entire space of the Box modifier = Modifier.fillMaxSize(), // Fill the entire space of the Box
verticalArrangement = Arrangement.spacedBy(4.dp, alignment = Alignment.Bottom) verticalArrangement = Arrangement.spacedBy(4.dp, alignment = Alignment.Bottom),
reverseLayout = true
) { ) {
item { Spacer(Modifier.height(innerPadding.calculateTopPadding())) } // Spacer for TopAppBar item { Spacer(Modifier.height(innerPadding.calculateBottomPadding())) } // Spacer for chat input
items( items(
items = panelState.messages, items = panelState.messages.reversed(),
key = { it.id } key = { it.id }
) { message -> ) { message ->
val isAuthor = message.user_id == currentUserId val isAuthor = message.user_id == currentUserId
@@ -349,7 +350,7 @@ fun ChatScreen(
} }
} }
item { Spacer(Modifier.height(innerPadding.calculateBottomPadding())) } // Spacer for chat input item { Spacer(Modifier.height(innerPadding.calculateTopPadding())) } // Spacer for TopAppBar
} }
} }
@@ -40,13 +40,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pr0gramm3r101.utils.navigateAndWipeBackStack import com.pr0gramm3r101.utils.navigateAndWipeBackStack
import com.pr0gramm3r101.utils.storage.ServerConfigData
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
import ru.fromchat.Res import ru.fromchat.Res
import ru.fromchat.api.ApiClient import ru.fromchat.api.ApiClient
import ru.fromchat.api.WebSocketManager import ru.fromchat.api.WebSocketManager
import ru.fromchat.back import ru.fromchat.back
import ru.fromchat.core.ServerConfigData
import ru.fromchat.core.config.Config import ru.fromchat.core.config.Config
import ru.fromchat.https_enabled import ru.fromchat.https_enabled
import ru.fromchat.save_continue import ru.fromchat.save_continue
@@ -1,7 +1,8 @@
package ru.fromchat package ru.fromchat
import ru.fromchat.ui.App
import androidx.compose.ui.window.ComposeUIViewController import androidx.compose.ui.window.ComposeUIViewController
import ru.fromchat.ui.App
@Suppress("unused")
fun MainViewController() = ComposeUIViewController { App() } fun MainViewController() = ComposeUIViewController { App() }
@@ -720,6 +720,7 @@ val ActivityInfo.launchIntent get() = Intent().apply {
flags = FLAG_ACTIVITY_NEW_TASK flags = FLAG_ACTIVITY_NEW_TASK
} }
@SuppressLint("QueryPermissionsNeeded")
@RequiresPermission(Manifest.permission.QUERY_ALL_PACKAGES) @RequiresPermission(Manifest.permission.QUERY_ALL_PACKAGES)
fun ActivityInfo.isLauncher(context: Context): Boolean { fun ActivityInfo.isLauncher(context: Context): Boolean {
with (context) { with (context) {
@@ -93,4 +93,15 @@ class AndroidSettings(): Settings {
preferences.remove(stringSetPreferencesKey(key)) preferences.remove(stringSetPreferencesKey(key))
} }
} }
override suspend fun contains(key: String): Boolean {
return dataStore.data.map { preferences ->
preferences.contains(stringPreferencesKey(key)) ||
preferences.contains(intPreferencesKey(key)) ||
preferences.contains(longPreferencesKey(key)) ||
preferences.contains(floatPreferencesKey(key)) ||
preferences.contains(booleanPreferencesKey(key)) ||
preferences.contains(stringSetPreferencesKey(key))
}.first()
}
} }
@@ -1,128 +0,0 @@
package com.pr0gramm3r101.utils.settings
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
class AndroidAsyncSettings : AsyncSettings {
private val dataStore: DataStore<Preferences>
get() = DataStoreSingleton.dataStore
override suspend fun putString(key: String, value: String) {
dataStore.edit { preferences ->
preferences[stringPreferencesKey(key)] = value
}
}
override suspend fun getString(key: String, default: String): String {
return dataStore.data.map { preferences ->
preferences[stringPreferencesKey(key)] ?: default
}.first()
}
override suspend fun putInt(key: String, value: Int) {
dataStore.edit { preferences ->
preferences[intPreferencesKey(key)] = value
}
}
override suspend fun getInt(key: String, default: Int): Int {
return dataStore.data.map { preferences ->
preferences[intPreferencesKey(key)] ?: default
}.first()
}
override suspend fun putLong(key: String, value: Long) {
dataStore.edit { preferences ->
preferences[longPreferencesKey(key)] = value
}
}
override suspend fun getLong(key: String, default: Long): Long {
return dataStore.data.map { preferences ->
preferences[longPreferencesKey(key)] ?: default
}.first()
}
override suspend fun putFloat(key: String, value: Float) {
dataStore.edit { preferences ->
preferences[floatPreferencesKey(key)] = value
}
}
override suspend fun getFloat(key: String, default: Float): Float {
return dataStore.data.map { preferences ->
preferences[floatPreferencesKey(key)] ?: default
}.first()
}
override suspend fun putBoolean(key: String, value: Boolean) {
dataStore.edit { preferences ->
preferences[booleanPreferencesKey(key)] = value
}
}
override suspend fun getBoolean(key: String, default: Boolean): Boolean {
return dataStore.data.map { preferences ->
preferences[booleanPreferencesKey(key)] ?: default
}.first()
}
override suspend fun putStringSet(key: String, value: Set<String>) {
dataStore.edit { preferences ->
preferences[stringSetPreferencesKey(key)] = value
}
}
override suspend fun getStringSet(key: String, default: Set<String>): Set<String> {
return dataStore.data.map { preferences ->
preferences[stringSetPreferencesKey(key)] ?: default
}.first()
}
override suspend fun putStringList(key: String, value: List<String>) {
putStringSet(key, value.toSet())
}
override suspend fun getStringList(key: String, default: List<String>): List<String> {
return getStringSet(key, default.toSet()).toList()
}
override suspend fun remove(key: String) {
dataStore.edit { preferences ->
preferences.remove(stringPreferencesKey(key))
preferences.remove(intPreferencesKey(key))
preferences.remove(longPreferencesKey(key))
preferences.remove(floatPreferencesKey(key))
preferences.remove(booleanPreferencesKey(key))
preferences.remove(stringSetPreferencesKey(key))
}
}
override suspend fun contains(key: String): Boolean {
return dataStore.data.map { preferences ->
preferences.contains(stringPreferencesKey(key)) ||
preferences.contains(intPreferencesKey(key)) ||
preferences.contains(longPreferencesKey(key)) ||
preferences.contains(floatPreferencesKey(key)) ||
preferences.contains(booleanPreferencesKey(key)) ||
preferences.contains(stringSetPreferencesKey(key))
}.first()
}
override suspend fun clear() {
dataStore.edit { preferences ->
preferences.clear()
}
}
}
actual val asyncSettings: AsyncSettings = AndroidAsyncSettings()
@@ -1,3 +1,5 @@
package com.pr0gramm3r101.utils.settings package com.pr0gramm3r101.utils.settings
actual val settings: Settings get() = AndroidSettings() actual val settings: Settings get() = AndroidSettings()
actual val secureSettings: Settings
get() = TODO("Not yet implemented")
@@ -1,37 +0,0 @@
package com.pr0gramm3r101.utils.settings
/**
* Async version of Settings API using suspend functions
*/
interface AsyncSettings {
suspend fun putString(key: String, value: String)
suspend fun getString(key: String, default: String = ""): String
suspend fun putInt(key: String, value: Int)
suspend fun getInt(key: String, default: Int = 0): Int
suspend fun putLong(key: String, value: Long)
suspend fun getLong(key: String, default: Long = 0L): Long
suspend fun putFloat(key: String, value: Float)
suspend fun getFloat(key: String, default: Float = 0f): Float
suspend fun putBoolean(key: String, value: Boolean)
suspend fun getBoolean(key: String, default: Boolean = false): Boolean
suspend fun putStringSet(key: String, value: Set<String>)
suspend fun getStringSet(key: String, default: Set<String> = emptySet()): Set<String>
suspend fun putStringList(key: String, value: List<String>)
suspend fun getStringList(key: String, default: List<String> = emptyList()): List<String>
suspend fun remove(key: String)
suspend fun contains(key: String): Boolean
suspend fun clear()
}
/**
* Global async settings instance
*/
expect val asyncSettings: AsyncSettings
@@ -30,4 +30,6 @@ interface Settings {
suspend fun getStringList(key: String, default: List<String> = emptyList()): List<String> suspend fun getStringList(key: String, default: List<String> = emptyList()): List<String>
suspend fun remove(key: String) suspend fun remove(key: String)
suspend fun contains(key: String): Boolean
} }
@@ -1,3 +1,4 @@
package com.pr0gramm3r101.utils.settings package com.pr0gramm3r101.utils.settings
expect val settings: Settings expect val settings: Settings
expect val secureSettings: Settings
@@ -1,29 +0,0 @@
package com.pr0gramm3r101.utils.storage
/**
* Authentication token storage
*/
object AuthStorage {
private val storage = NamespacedStorage("auth")
suspend fun getToken(): String? {
val token = storage.getString("token")
return token.ifEmpty { null }
}
suspend fun setToken(token: String?) {
if (token != null) {
storage.putString("token", token)
} else {
storage.remove("token")
}
}
suspend fun clearToken() {
storage.remove("token")
}
suspend fun putString(key: String, value: String) = storage.putString(key, value)
suspend fun getString(key: String, default: String = ""): String = storage.getString(key, default)
suspend fun remove(key: String) = storage.remove(key)
}
@@ -1,52 +0,0 @@
package com.pr0gramm3r101.utils.storage
/**
* Server configuration data
*/
data class ServerConfigData(
val serverUrl: String,
val httpsEnabled: Boolean
)
/**
* Server configuration storage
*/
object ServerConfigStorage {
private val storage = NamespacedStorage("server_config")
private const val SERVER_URL_KEY = "server_url"
private const val HTTPS_ENABLED_KEY = "https_enabled"
suspend fun getServerUrl() = storage.getString(SERVER_URL_KEY).ifEmpty { null }
suspend fun setServerUrl(url: String) = storage.putString(SERVER_URL_KEY, url)
suspend fun getHttpsEnabled() =
if (storage.contains(HTTPS_ENABLED_KEY))
storage.getBoolean(HTTPS_ENABLED_KEY, true)
else null
suspend fun setHttpsEnabled(enabled: Boolean) {
storage.putBoolean(HTTPS_ENABLED_KEY, enabled)
}
suspend fun hasConfiguration() = getServerUrl() != null && getHttpsEnabled() != null
suspend fun getConfig(): ServerConfigData {
var url = getServerUrl()
var https = getHttpsEnabled()
if (url == null || https == null) {
url = "fromchat.ru"
https = true
saveConfig(ServerConfigData(url, https))
}
return ServerConfigData(url, https)
}
suspend fun saveConfig(config: ServerConfigData) {
setServerUrl(config.serverUrl)
setHttpsEnabled(config.httpsEnabled)
}
}
@@ -1,59 +0,0 @@
package com.pr0gramm3r101.utils.storage
import com.pr0gramm3r101.utils.settings.AsyncSettings
import com.pr0gramm3r101.utils.settings.asyncSettings
/**
* Generic key-value storage using AsyncSettings
*/
class Storage(private val settings: AsyncSettings = asyncSettings) {
suspend fun putString(key: String, value: String) = settings.putString(key, value)
suspend fun getString(key: String, default: String = ""): String = settings.getString(key, default)
suspend fun putInt(key: String, value: Int) = settings.putInt(key, value)
suspend fun getInt(key: String, default: Int = 0): Int = settings.getInt(key, default)
suspend fun putLong(key: String, value: Long) = settings.putLong(key, value)
suspend fun getLong(key: String, default: Long = 0L): Long = settings.getLong(key, default)
suspend fun putFloat(key: String, value: Float) = settings.putFloat(key, value)
suspend fun getFloat(key: String, default: Float = 0f): Float = settings.getFloat(key, default)
suspend fun putBoolean(key: String, value: Boolean) = settings.putBoolean(key, value)
suspend fun getBoolean(key: String, default: Boolean = false): Boolean = settings.getBoolean(key, default)
suspend fun putStringSet(key: String, value: Set<String>) = settings.putStringSet(key, value)
suspend fun getStringSet(key: String, default: Set<String> = emptySet()): Set<String> = settings.getStringSet(key, default)
suspend fun putStringList(key: String, value: List<String>) = settings.putStringList(key, value)
suspend fun getStringList(key: String, default: List<String> = emptyList()): List<String> = settings.getStringList(key, default)
suspend fun remove(key: String) = settings.remove(key)
suspend fun contains(key: String): Boolean = settings.contains(key)
suspend fun clear() = settings.clear()
}
/**
* Namespaced storage for specific features
*/
class NamespacedStorage(private val prefix: String, private val storage: Storage = Storage()) {
private fun key(name: String) = "$prefix:$name"
suspend fun putString(name: String, value: String) = storage.putString(key(name), value)
suspend fun getString(name: String, default: String = ""): String = storage.getString(key(name), default)
suspend fun putInt(name: String, value: Int) = storage.putInt(key(name), value)
suspend fun getInt(name: String, default: Int = 0): Int = storage.getInt(key(name), default)
suspend fun putLong(name: String, value: Long) = storage.putLong(key(name), value)
suspend fun getLong(name: String, default: Long = 0L): Long = storage.getLong(key(name), default)
suspend fun putFloat(name: String, value: Float) = storage.putFloat(key(name), value)
suspend fun getFloat(name: String, default: Float = 0f): Float = storage.getFloat(key(name), default)
suspend fun putBoolean(name: String, value: Boolean) = storage.putBoolean(key(name), value)
suspend fun getBoolean(name: String, default: Boolean = false): Boolean = storage.getBoolean(key(name), default)
suspend fun remove(name: String) = storage.remove(key(name))
suspend fun contains(name: String): Boolean = storage.contains(key(name))
}
@@ -1,99 +0,0 @@
package com.pr0gramm3r101.utils.settings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import platform.Foundation.NSBundle
import platform.Foundation.NSUserDefaults
class IosAsyncSettings : AsyncSettings {
private val defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults
override suspend fun putString(key: String, value: String): Unit = withContext(Dispatchers.Default) {
defaults.setObject(value, key)
defaults.synchronize()
}
override suspend fun getString(key: String, default: String): String = withContext(Dispatchers.Default) {
defaults.stringForKey(key) ?: default
}
override suspend fun putInt(key: String, value: Int): Unit = withContext(Dispatchers.Default) {
defaults.setInteger(value.toLong(), key)
defaults.synchronize()
}
override suspend fun getInt(key: String, default: Int): Int = withContext(Dispatchers.Default) {
defaults.integerForKey(key).toInt()
}
override suspend fun putLong(key: String, value: Long): Unit = withContext(Dispatchers.Default) {
defaults.setObject(value, key)
defaults.synchronize()
}
override suspend fun getLong(key: String, default: Long): Long = withContext(Dispatchers.Default) {
(defaults.objectForKey(key) as? platform.Foundation.NSNumber)?.longValue ?: default
}
override suspend fun putFloat(key: String, value: Float): Unit = withContext(Dispatchers.Default) {
defaults.setFloat(value, key)
defaults.synchronize()
}
override suspend fun getFloat(key: String, default: Float): Float = withContext(Dispatchers.Default) {
defaults.floatForKey(key)
}
override suspend fun putBoolean(key: String, value: Boolean): Unit = withContext(Dispatchers.Default) {
defaults.setBool(value, key)
defaults.synchronize()
}
override suspend fun getBoolean(key: String, default: Boolean): Boolean = withContext(Dispatchers.Default) {
if (defaults.objectForKey(key) != null) {
defaults.boolForKey(key)
} else {
default
}
}
override suspend fun putStringSet(key: String, value: Set<String>): Unit = withContext(Dispatchers.Default) {
defaults.setObject(value.toList(), key)
defaults.synchronize()
}
override suspend fun getStringSet(key: String, default: Set<String>): Set<String> = withContext(Dispatchers.Default) {
val list = defaults.objectForKey(key) as? List<*> ?: return@withContext default
list.filterIsInstance<String>().toSet()
}
override suspend fun putStringList(key: String, value: List<String>): Unit = withContext(Dispatchers.Default) {
defaults.setObject(value, key)
defaults.synchronize()
}
override suspend fun getStringList(key: String, default: List<String>): List<String> = withContext(Dispatchers.Default) {
val list = defaults.objectForKey(key) as? List<*> ?: return@withContext default
list.filterIsInstance<String>()
}
override suspend fun remove(key: String): Unit = withContext(Dispatchers.Default) {
defaults.removeObjectForKey(key)
defaults.synchronize()
}
override suspend fun contains(key: String): Boolean = withContext(Dispatchers.Default) {
defaults.objectForKey(key) != null
}
override suspend fun clear(): Unit = withContext(Dispatchers.Default) {
val bundleId = NSBundle.mainBundle.bundleIdentifier ?: ""
val domain = defaults.persistentDomainForName(bundleId)
domain?.keys?.forEach { key ->
defaults.removeObjectForKey(key as String)
}
defaults.synchronize()
}
}
actual val asyncSettings: AsyncSettings = IosAsyncSettings()
@@ -1,5 +1,7 @@
package com.pr0gramm3r101.utils.settings package com.pr0gramm3r101.utils.settings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import platform.Foundation.NSUserDefaults import platform.Foundation.NSUserDefaults
// TODO fix // TODO fix
@@ -42,4 +44,8 @@ class IosSettings : Settings {
} }
override suspend fun remove(key: String) = defaults.removeObjectForKey(key) override suspend fun remove(key: String) = defaults.removeObjectForKey(key)
override suspend fun contains(key: String): Boolean = withContext(Dispatchers.Default) {
defaults.objectForKey(key) != null
}
} }
@@ -1,3 +1,5 @@
package com.pr0gramm3r101.utils.settings package com.pr0gramm3r101.utils.settings
actual val settings: Settings get() = IosSettings() actual val settings: Settings get() = IosSettings()
actual val secureSettings: Settings
get() = TODO("Not yet implemented")