Skip to content

Quickstart

Get a working repository in under five minutes.

1. Apply the plugin

In your module's build.gradle.kts:

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android") version "2.3.20"
    id("io.github.sufarook.kiln") version "1.0.0-alpha04" // (1)!
}

dependencies {
    implementation("io.github.sufarook.kiln:annotations:1.0.0-alpha04")
    implementation("io.github.sufarook.kiln:runtime:1.0.0-alpha04")
    implementation("app.cash.sqldelight:android-driver:2.3.2")
}
  1. The plugin automatically applies KSP and wires the generated sources. No manual KSP configuration needed.

2. Annotate a data class

@DbEntity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val title: String,
    val body: String = "",
    @Column(name = "is_pinned") val isPinned: Boolean = false
)

Rebuild the project. Kiln generates NoteRepository and NoteColumns in build/generated/.

Tip

If the IDE shows an unresolved reference on NoteRepository, run Build → Make Project once. The class is generated during compilation — the IDE resolves it after the first successful build.

3. Initialize

Call createTable() once per repository at app startup. It is safe to call on every launch — it creates the table if it doesn't exist and auto-migrates the schema if the entity changed.

class MyApp : Application() {
    lateinit var noteRepo: NoteRepository

    override fun onCreate() {
        super.onCreate()
        val driver = AndroidDatabaseDriverFactory(this).create("myapp.db")
        noteRepo = NoteRepository(driver).also { it.createTable() }
    }
}

4. Write and observe

class NotesActivity : AppCompatActivity() {

    private val repo get() = (application as MyApp).noteRepo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe — re-emits automatically after every insert/update/delete
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                repo.observeAll().collect { notes ->
                    adapter.submitList(notes)
                }
            }
        }

        // Insert
        btnAdd.setOnClickListener {
            lifecycleScope.launch {
                repo.insert(Note(title = etTitle.text.toString()))
            }
        }

        // Update (use data class copy for partial changes)
        fun togglePin(note: Note) {
            lifecycleScope.launch {
                repo.update(note.copy(isPinned = !note.isPinned))
            }
        }

        // Delete
        fun delete(note: Note) {
            lifecycleScope.launch { repo.delete(note.id) }
        }
    }
}

That's it

You now have a fully reactive, type-safe, auto-migrating SQLite repository with no SQL written and no version numbers to track.

Next steps