Skip to content

Initialization

Create driver and repositories

Create one KilnDriver and pass it to each repository. All repositories share the same driver — SQLite is a single-file database and a single KilnDriver instance manages the connection.

Kiln generates a KilnSchema object covering every @DbEntity in the module, so one call creates the whole schema:

TaskTrackerApp.kt
class TaskTrackerApp : Application() {

    lateinit var projectRepo: ProjectRepository
    lateinit var taskRepo: TaskRepository
    lateinit var checklistRepo: ChecklistItemRepository

    override fun onCreate() {
        super.onCreate()
        val driver = AndroidDatabaseDriverFactory(this).create("tasktracker.db")

        // Creates and auto-migrates every table. Add a new @DbEntity later and
        // this line does not change.
        KilnSchema.createAll(driver)

        projectRepo   = ProjectRepository(driver)
        taskRepo      = TaskRepository(driver)
        checklistRepo = ChecklistItemRepository(driver)
    }
}

KilnSchema is generated into the deepest package your entities share — if they all live in com.example.data, it is com.example.data.KilnSchema.

Creating tables individually

createAll() is a convenience, not a replacement. Every repository still has its own createTable(), which is what you want if a table should only exist in certain builds or you need to control exactly when a migration runs:

projectRepo = ProjectRepository(driver).also { it.createTable() }

Register in AndroidManifest.xml:

AndroidManifest.xml
<application
    android:name=".TaskTrackerApp"
    ...>

What table creation actually does

KilnSchema.createAll() calls createTable() on each repository, and createTable() does two things every time it runs:

  1. Runs SchemaMigrator.sync() — diffs the live schema against the generated column list and migrates if needed. No-op if the table doesn't exist yet.
  2. Runs CREATE TABLE IF NOT EXISTS — safe to call repeatedly; no-op if the table exists.

Call it on every launch. It is fast when the schema hasn't changed (just a PRAGMA table_info read) and performs the necessary migration when it has.

Creation order does not matter

Kiln does not emit FOREIGN KEY constraints, so SQLite has no notion that one of your tables references another — no table needs to exist before any other. @Relation generates query helpers (findByProject, deleteByProject, …), not database-level constraints. Referential integrity is handled in your own code; see Cross-table Loading.

Access from Activities and ViewModels

// From an Activity
val app = application as TaskTrackerApp
val taskRepo = app.taskRepo

// From a ViewModel (inject via factory or DI)
class ProjectViewModel(private val projectRepo: ProjectRepository) : ViewModel()

iOS (KMP)

On iOS, use IosDatabaseDriverFactory in place of the Android one:

iosMain
val driver = IosDatabaseDriverFactory().create("tasktracker.db")
val projectRepo = ProjectRepository(driver).also { it.createTable() }