---
title: "SQLDelight com Kotlin Multiplatform: Android, iOS e SQLite em 2026 | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/sqldelight-kotlin-multiplatform-android-ios-2026/"
markdown_url: "https://kotlin.dev.br/blog/sqldelight-kotlin-multiplatform-android-ios-2026.MD"
description: "Aprenda SQLDelight com Kotlin Multiplatform para compartilhar persistência SQLite entre Android e iOS com queries tipadas, Flow e migrations."
date: "2026-05-31"
author: "Karina Melo"
---

# SQLDelight com Kotlin Multiplatform: Android, iOS e SQLite em 2026 | Kotlin Brasil

Aprenda SQLDelight com Kotlin Multiplatform para compartilhar persistência SQLite entre Android e iOS com queries tipadas, Flow e migrations.


SQLDelight é uma das escolhas mais pragmáticas para persistência local em projetos Kotlin Multiplatform. Em vez de tentar esconder o SQLite atrás de um ORM pesado, ele parte de uma ideia simples: você escreve SQL real em arquivos `.sq`, o plugin valida as queries e gera uma API Kotlin type-safe para Android, iOS, desktop ou outro target suportado.

Para quem já usa [Room com Kotlin](/tutoriais/kotlin-room-database-tutorial/) no Android, a mudança de mentalidade é importante. Room é excelente quando o app é Android-only e você quer integração direta com Jetpack. SQLDelight brilha quando a camada de dados precisa viver no módulo `shared` de um projeto [Kotlin Multiplatform](/tutoriais/kotlin-multiplatform-tutorial/), principalmente quando Android e iOS devem compartilhar regras de cache, queries, migrations e testes.

Este guia mostra como pensar SQLDelight em 2026: onde ele entra na arquitetura KMP, como configurar o módulo compartilhado, como escrever queries, como observar dados com Flow e quais cuidados tomar antes de levar para produção.

## Quando SQLDelight faz sentido

Use SQLDelight quando o produto precisa de persistência estruturada e a lógica de dados deve ser compartilhada entre plataformas. Exemplos comuns:

- app Android e iOS com cache offline de catálogo, tarefas, pedidos ou mensagens;
- camada de favoritos, histórico ou fila de sincronização no módulo compartilhado;
- produto que usa [Ktor Client](/blog/ktor-client-resiliente-timeout-retry-circuit-breaker-2026/) em `commonMain` e precisa persistir respostas normalizadas;
- arquitetura [offline-first com Kotlin](/blog/android-offline-first-kotlin-2026/) que não deve duplicar regras em Swift e Kotlin Android;
- app KMP que quer testar repository, migrations e queries sem depender da UI nativa.

Se o app é apenas Android, Room continua sendo uma ótima escolha. Se você só precisa salvar tema, onboarding, idioma ou filtros simples, [DataStore Preferences](/tutoriais/datastore-preferences-kotlin/) é menor e mais direto. SQLDelight entra quando os dados têm tabelas, relacionamentos, busca, ordenação, paginação ou sincronização.

## Como organizar no módulo shared

Em um projeto KMP moderno, mantenha SQLDelight dentro do módulo compartilhado. A aplicação Android e o app iOS apenas fornecem o driver específico de plataforma.

```kotlin
plugins {
    kotlin("multiplatform")
    id("com.android.library")
    id("app.cash.sqldelight") version "2.0.2"
}

kotlin {
    androidTarget()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("app.cash.sqldelight:runtime:2.0.2")
                implementation("app.cash.sqldelight:coroutines-extensions:2.0.2")
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("app.cash.sqldelight:android-driver:2.0.2")
            }
        }
        val iosMain by getting {
            dependencies {
                implementation("app.cash.sqldelight:native-driver:2.0.2")
            }
        }
    }
}

sqldelight {
    databases {
        create("AppDatabase") {
            packageName.set("br.dev.kotlin.shared.db")
        }
    }
}
```

Confira sempre as versões compatíveis com seu Kotlin, Gradle e Android Gradle Plugin. Em times maiores, centralize versões no version catalog para evitar que Android e iOS compilem contra combinações diferentes.

## Criando o schema em arquivos .sq

SQLDelight usa arquivos `.sq` como fonte de verdade. Um arquivo `Tarefa.sq` pode definir tabela e queries ao mesmo tempo:

```sql
CREATE TABLE tarefa (
  id INTEGER NOT NULL PRIMARY KEY,
  titulo TEXT NOT NULL,
  concluida INTEGER AS Boolean NOT NULL DEFAULT 0,
  atualizada_em INTEGER NOT NULL
);

listarTodas:
SELECT * FROM tarefa
ORDER BY atualizada_em DESC;

buscarPorId:
SELECT * FROM tarefa
WHERE id = ?;

salvar:
INSERT OR REPLACE INTO tarefa(id, titulo, concluida, atualizada_em)
VALUES (?, ?, ?, ?);

marcarConcluida:
UPDATE tarefa
SET concluida = ?, atualizada_em = ?
WHERE id = ?;
```

O plugin gera código Kotlin para chamar essas queries. Isso reduz uma classe inteira de bugs: nome de coluna errado, tipo incompatível, parâmetro ausente e query quebrada aparecem em build, não só em produção.

## Repository compartilhado com Flow

No `commonMain`, você pode expor dados como Flow e deixar cada plataforma reagir do seu jeito. Android coleta no ViewModel; iOS pode adaptar para Swift usando a estratégia de interop do projeto.

```kotlin
class TarefasRepository(
    private val database: AppDatabase,
    private val clock: Clock,
) {
    fun observarTarefas(): Flow<List<Tarefa>> =
        database.tarefaQueries
            .listarTodas()
            .asFlow()
            .mapToList(Dispatchers.Default)
            .map { linhas -> linhas.map { it.toDomain() } }

    fun salvar(tarefa: Tarefa) {
        database.tarefaQueries.salvar(
            id = tarefa.id,
            titulo = tarefa.titulo,
            concluida = tarefa.concluida,
            atualizada_em = clock.now().toEpochMilliseconds(),
        )
    }
}
```

Repare que o repository não sabe se está rodando no Android ou no iOS. Ele só conhece o banco gerado, modelos de domínio e regras do produto. Essa separação combina com [MVVM em Kotlin](/tutoriais/kotlin-mvvm-tutorial/) e com módulos compartilhados focados em lógica, não em detalhes de tela.

## Drivers por plataforma

O ponto específico de plataforma é a criação do driver. No Android, você usa `AndroidSqliteDriver`:

```kotlin
actual class DatabaseDriverFactory(
    private val context: Context,
) {
    actual fun createDriver(): SqlDriver =
        AndroidSqliteDriver(AppDatabase.Schema, context, "app.db")
}
```

No iOS, use o driver nativo:

```kotlin
actual class DatabaseDriverFactory {
    actual fun createDriver(): SqlDriver =
        NativeSqliteDriver(AppDatabase.Schema, "app.db")
}
```

Esse padrão `expect`/`actual` mantém a inicialização separada e permite que o código comum receba apenas `AppDatabase(driver)`.

## Migrations sem improviso

SQLDelight valoriza migrations explícitas. Quando o schema evolui, crie arquivos como `1.sqm`, `2.sqm` e mantenha a sequência versionada no repositório.

```sql
ALTER TABLE tarefa ADD COLUMN prioridade INTEGER NOT NULL DEFAULT 0;

CREATE INDEX tarefa_prioridade_idx ON tarefa(prioridade);
```

Evite apagar banco em desenvolvimento como solução padrão. Isso esconde problemas que usuários reais terão ao atualizar o app. Teste migration com dados representativos, principalmente quando há filas offline, IDs temporários, relacionamentos ou campos usados por sincronização.

## SQLDelight em arquitetura offline-first

Em uma arquitetura offline-first, SQLDelight normalmente guarda três tipos de dados:

- entidades exibidas na UI, como tarefas, produtos, mensagens ou pedidos;
- metadados de sincronização, como `updatedAt`, `syncStatus` e `deletedPendingSync`;
- fila de operações pendentes para enviar ao backend quando houver rede.

O repository lê primeiro do banco local, grava mudanças localmente e agenda sincronização com uma camada específica de plataforma, como WorkManager no Android. O importante é que a regra de negócio continue no módulo compartilhado. Assim, Android e iOS tomam as mesmas decisões sobre conflito, retry e merge.

## Armadilhas comuns

A primeira armadilha é tratar SQLDelight como se fosse Room. Em SQLDelight, SQL é parte central do design. Use isso a favor do projeto: escreva queries claras, índices explícitos e migrations revisáveis.

A segunda é compartilhar demais. Nem todo detalhe precisa ir para `commonMain`. Se uma tela iOS usa cache temporário próprio ou se Android depende de API específica do Jetpack, mantenha essa decisão na plataforma. Compartilhe o domínio que realmente precisa ser consistente.

A terceira é ignorar observabilidade. Bugs de persistência local são difíceis de reproduzir. Registre versão do schema, estado de sincronização e falhas de migration em logs seguros. Para apps críticos, adicione métricas de fila pendente e tempo desde a última sincronização.

## Próximos passos

Se você está começando agora, implemente um banco pequeno com duas tabelas, uma migration e um repository compartilhado. Depois conecte esse repository a uma tela Android e a uma tela iOS simples. Esse exercício ensina mais do que copiar uma arquitetura grande pronta.

Para aprofundar, combine este guia com a comparação [Room vs SQLDelight](/comparacoes/room-vs-sqldelight/), o guia de [Kotlin Multiplatform Mobile](/guias/guia-kotlin-multiplatform-mobile/), a trilha de [Android offline-first](/blog/android-offline-first-kotlin-2026/) e o conteúdo sobre [KMP em 2026](/blog/kmp-nova-estrutura-projetos-2026/). Juntos, eles formam uma base sólida para entregar persistência local compartilhada sem sacrificar qualidade nativa.
