Platform setup

Table of contents
  1. Android
    1. Where to instantiate
    2. Database file
  2. JVM
    1. In-memory (default)
    2. File-backed
  3. Threading
  4. iOS / Native
  5. Next

Sqkon ships a single Maven artifact with platform-specific Sqkon(...) factory functions. This page covers the construction details for each target and the threading rules that apply everywhere.

Android

The Android factory needs a Context and a CoroutineScope:

fun Sqkon(
    context: Context,
    scope: CoroutineScope,
    json: Json = SqkonJson { },
    dbFileName: String? = "sqkon.db",
    config: KeyValueStorage.Config = KeyValueStorage.Config(),
): Sqkon

Where to instantiate

Treat the Sqkon instance like a database connection pool: create one per database for the lifetime of your process. The natural place is your Application subclass:

class MyApplication : Application() {
    private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)

    val sqkon: Sqkon by lazy {
        Sqkon(context = this, scope = appScope)
    }
}

Inject sqkon (or specific KeyValueStorage<T> instances) into your DI graph from there.

Database file

By default Sqkon writes to sqkon.db inside your app’s standard database directory (the same place Room and SQLDelight Android drivers use). To pick a different file name, pass dbFileName:

Sqkon(context = this, scope = appScope, dbFileName = "my-cache.db")

To run in-memory — useful for instrumented tests or transient caches that should not survive a process restart — pass null:

Sqkon(context = this, scope = appScope, dbFileName = null)

An older overload accepted inMemory: Boolean and is now deprecated. Migrate to dbFileName = null (or omit the parameter to use the default "sqkon.db"); the deprecated overload simply forwards to the new one.

JVM

The JVM factory takes a CoroutineScope and an AndroidxSqliteDatabaseType:

fun Sqkon(
    scope: CoroutineScope,
    json: Json = SqkonJson { },
    type: AndroidxSqliteDatabaseType = AndroidxSqliteDatabaseType.Memory,
    config: KeyValueStorage.Config = KeyValueStorage.Config(),
): Sqkon

In-memory (default)

The default is AndroidxSqliteDatabaseType.Memory, which is ideal for unit tests:

val sqkon = Sqkon(scope = scope) // in-memory by default

File-backed

For a persistent JVM database, point at a file:

import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteDatabaseType

val sqkon = Sqkon(
    scope = appScope,
    type = AndroidxSqliteDatabaseType.File("data/sqkon.db"),
)

The path is resolved relative to the JVM’s working directory — pass an absolute path if that is ambiguous in your environment.

Threading

Sqkon manages its own dispatchers internally. You don’t need to wrap calls in withContext(Dispatchers.IO) — the library does it for you.

Internally:

  • Reads run on a Dispatchers.IO-based dispatcher with limited parallelism = 4, matching SQLite’s default WAL connection pool size.
  • Writes run on a Dispatchers.IO-based dispatcher with limited parallelism = 1 (SQLite allows only one writer at a time).

This is configured in Sqkon.kt and the platform SqkonDatabaseDriver files.

Do not enable SQLDelight’s generateAsync = true. The async driver is effectively broken on multithreaded platforms with coroutines. Sqkon relies on the synchronous driver and serializes work through its own dispatchers — that is the supported configuration. This rule is enforced in the project’s library/build.gradle.kts and called out in CLAUDE.md.

The CoroutineScope you pass to Sqkon(...) is used as the parent of the internal reactive query coroutines. Cancel that scope (typically only in tests or when shutting down a worker process) to release database resources.

iOS / Native

Native targets are not currently supported. Sqkon depends on the sqldelight-androidx-driver which is JVM/Android-only at the time of writing.

If iOS support matters to you, please open or upvote an issue on the GitHub repo — there’s no fundamental blocker beyond a native driver wiring.

Next