1. Antes de começar
Pré-requisitos
- Noções básicas do Kotlin Multiplatform.
- Experiência com Kotlin.
- Conhecimentos básicos da sintaxe Swift (link em inglês).
- O Xcode e o simulador do iOS (link em inglês) instalados.
O que você precisa
- A versão estável mais recente do Android Studio.
- Um computador Mac com um sistema macOS.
- Xcode 16.1 (link em inglês) e simulador de iPhone com iOS 16.0 ou versão mais recente.
O que você vai aprender
- Como compartilhar um banco de dados do Room entre um app Android e um app iOS.
2. Começar a configuração
Para começar, siga estas etapas:
- Clone o repositório do GitHub com este comando de terminal:
$ git clone https://github.com/android/codelab-android-kmp.git
Se preferir, faça o download do repositório como um arquivo ZIP:
- No Android Studio, abra o projeto
migrate-room, que contém as ramificações abaixo:
main: contém o código inicial do projeto, em que você vai fazer mudanças para concluir o codelab.end: contém o código da solução do codelab.
É recomendado começar pela ramificação main e seguir cada etapa do codelab no seu próprio ritmo.
- Para conferir o código da solução, execute este comando:
$ git clone -b end https://github.com/android/codelab-android-kmp.git
Você também pode baixar o código da solução:
3. Detalhes sobre o app de exemplo
Este tutorial consiste no aplicativo de exemplo Fruitties criado em estruturas nativas (Jetpack Compose no Android e SwiftUI no iOS).
O app Fruitties tem dois recursos principais:
- Uma lista de itens Fruit, cada um com um botão para adicionar o item a um carrinho.
- Um carrinho que aparece na parte de cima, mostrando quantas frutas foram adicionadas e as quantidades.

Arquitetura do app Android
O app Android segue as diretrizes oficiais de arquitetura do Android para manter uma estrutura clara e modular.

Arquitetura do app iOS

Módulo compartilhado do KMP
Este projeto já foi configurado com um módulo compartilhado para o KMP, embora esteja vazio no momento. Se o projeto ainda não tiver um módulo compartilhado, comece com o codelab Começar a usar o Kotlin Multiplatform.
4. Preparar o banco de dados do Room para a integração do KMP
Antes de mover o código do app Fruitties para o módulo shared, verifique se o app é compatível com as APIs Room do Kotlin Multiplatform (KMP). Esta seção explica esse processo.
Uma atualização importante é usar um driver SQLite compatível com Android e iOS. Para oferecer compatibilidade com a funcionalidade do banco de dados do Room em várias plataformas, use BundledSQLiteDriver. Esse driver agrupa o SQLite diretamente no aplicativo, o que o torna adequado para uso multiplataforma em Kotlin. Para ver orientações detalhadas, consulte o guia de migração do Room KMP.
Atualizar dependências
Primeiro, adicione as dependências room-runtime e sqlite-bundled ao arquivo libs.versions.toml:
# Add libraries
[libraries]
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidx-room" }
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidx-sqlite" }
Em seguida, atualize o arquivo build.gradle.kts do módulo :androidApp para usar essas dependências e remova o uso de libs.androidx.room.ktx:
// Add
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
// Remove
implementation(libs.androidx.room.ktx)
Agora, sincronize o projeto no Android Studio.
Modificar o módulo de banco de dados do BundledSQLiteDriver
Em seguida, modifique a lógica de criação do banco de dados no app Android para usar o BundledSQLiteDriver, tornando-o compatível com o KMP e mantendo a funcionalidade no Android.
- Abra o arquivo
DatabaseModule.ktlocalizado emandroidApp/src/main/kotlin/com/example/fruitties/kmptutorial/android/di/DatabaseModule.kt. - Atualize o método
providesAppDatabaseconforme o snippet abaixo:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
@Module
@InstallIn(SingletonComponent::class)
internal object DatabaseModule {
...
@Provides
@Singleton
fun providesAppDatabase(@ApplicationContext context: Context): AppDatabase {
val dbFile = context.getDatabasePath("sharedfruits.db")
return Room.databaseBuilder<AppDatabase>(context, dbFile.absolutePath)
.setDriver(BundledSQLiteDriver())
.build()
}
Criar e executar o app Android
Agora que você mudou o driver SQLite nativo para o agrupado, vamos verificar os builds do app e se tudo funciona corretamente antes de migrar o banco de dados para o módulo :shared.
5. Mover o código do banco de dados para o módulo :shared
Nesta etapa, vamos transferir a configuração do banco de dados do Room do app Android para o módulo :shared, permitindo que o banco de dados seja acessível para Android e iOS.
Atualizar a configuração build.gradle.kts do módulo :shared
Comece atualizando o arquivo build.gradle.kts do módulo :shared para usar dependências multiplataforma do Room.
- Adicione os plug-ins da KSP e do Room:
plugins {
...
// TODO add KSP + ROOM plugins
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
- Adicione as dependências
room-runtimeesqlite-bundledao blococommonMain:
sourceSets {
commonMain {
// TODO Add KMP dependencies here
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
}
}
- Inclua a configuração da KSP para cada plataforma de destino adicionando um novo bloco
dependenciesde nível superior. Para facilitar, adicione-o na parte de baixo do arquivo:
// Should be its own top level block. For convenience, add at the bottom of the file
dependencies {
add("kspAndroid", libs.androidx.room.compiler)
add("kspIosSimulatorArm64", libs.androidx.room.compiler)
add("kspIosX64", libs.androidx.room.compiler)
add("kspIosArm64", libs.androidx.room.compiler)
}
- Também no nível superior, adicione um novo bloco para definir o local do esquema
Room:
// Should be its own top level block. For convenience, add at the bottom of the file
room {
schemaDirectory("$projectDir/schemas")
}
- O Gradle sincroniza o projeto.
Mover o esquema do Room para o módulo :shared
Mova o diretório androidApp/schemas para a pasta raiz do módulo :shared ao lado da pasta src/:
De: 
Para: 
Mover DAOs e entidades
Agora que você adicionou as dependências do Gradle necessárias ao módulo compartilhado do KMP, é necessário mover os DAOs e entidades do módulo :androidApp para o módulo :shared.
Isso envolve mover os arquivos para os respectivos locais no conjunto de origem commonMain no módulo :shared.
Mover o modelo Fruittie
Você pode aproveitar a funcionalidade Refactor → Move para mudar entre módulos sem interromper as importações:
- Localize o arquivo
androidApp/src/main/kotlin/.../model/Fruittie.kt, clique com o botão direito do mouse e selecione Refactor → Move (ou pressione F6):
- Na caixa de diálogo Move, selecione o ícone
...ao lado do campo Destination directory.
- Selecione o conjunto de origem commonMain na caixa de diálogo Choose Destination Directory e clique em "OK". Talvez seja necessário desativar a caixa de seleção Show only existing source roots.

- Clique no botão Refactor para mover o arquivo.
Mover os modelos CartItem e CartItemWithFruittie
Para o arquivo androidApp/.../model/CartItem.kt, siga estas etapas:
- Abra o arquivo, clique com o botão direito do mouse na classe
CartIteme selecione Refactor > Move. - Isso abre a mesma caixa de diálogo Move, mas nesse caso, você também marca a caixa do membro
CartItemWithFruittie.
Selecione o ícone ...e o conjunto de origemcommonMain, assim como você fez com o arquivoFruittie.kt.
Mover DAOs e AppDatabase
Siga as mesmas etapas para os arquivos abaixo (você pode selecionar os três ao mesmo tempo):
androidApp/.../database/FruittieDao.ktandroidApp/.../database/CartDao.ktandroidApp/.../database/AppDatabase.kt
Atualizar o AppDatabase compartilhado para que ele funcione em várias plataformas
Agora que você moveu as classes de banco de dados para o módulo :shared, ajuste-as para gerar as implementações necessárias nas duas plataformas.
- Abra o arquivo
/shared/src/commonMain/kotlin/com/example/fruitties/kmptutorial/android/database/AppDatabase.kt. - Adicione esta implementação de
RoomDatabaseConstructor:
import androidx.room.RoomDatabaseConstructor
// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
override fun initialize(): AppDatabase
}
- Adicione a anotação
@ConstructedBy(AppDatabaseConstructor::class)à classeAppDatabase:
import androidx.room.ConstructedBy
@Database(
entities = [Fruittie::class, CartItem::class],
version = 1,
)
// TODO Add this line
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
...

Mover a criação do banco de dados para o módulo :shared
Agora, mova a configuração do Room específica do Android do módulo :androidApp para o módulo :shared. Isso é necessário porque, na próxima etapa, você vai remover a dependência Room do módulo :androidApp.
- Localize o arquivo
androidApp/.../di/DatabaseModule.kt. - Selecione o conteúdo da função
providesAppDatabase, clique com o botão direito do mouse e selecione Refactor > Extract Function to Scope:
- Selecione
DatabaseModule.ktno menu.
Isso move o conteúdo para uma função appDatabaseglobal. Pressione Enter para confirmar o nome da função.
- Remova o modificador de visibilidade
privatepara tornar a função pública. - Mova a função para o módulo
:sharedclicando com o botão direito do mouse em Refactor > Move. - Na caixa de diálogo Move, selecione o ícone ... ao lado do campo Destination directory.

- Na caixa de diálogo Choose Destination Directory, selecione o conjunto de origem shared > androidMain e a pasta /shared/src/androidMain/. Em seguida, clique em OK.

- Mude o sufixo no campo To package de
.dipara.database
- Clique em Refactor.
Limpeza do código desnecessário do :androidApp
Nesse ponto, você moveu o banco de dados do Room para o módulo multiplataforma, e nenhuma das dependências do Room é necessária no módulo :androidApp. Elas podem ser removidas.
- Abra o arquivo
build.gradle.ktsno módulo:androidApp. - Remova as dependências e a configuração, conforme mostrado no snippet abaixo:
plugins {
// TODO Remove
alias(libs.plugins.room)
}
android {
// TODO Remove
ksp {
arg("room.generateKotlin", "true")
}
dependencies {
// TODO Keep room-runtime
implementation(libs.androidx.room.runtime)
// TODO Remove
implementation(libs.androidx.sqlite.bundled)
ksp(libs.androidx.room.compiler)
}
// TODO Remove
room {
schemaDirectory("$projectDir/schemas")
}
- O Gradle sincroniza o projeto.
Criar e executar o app Android
Execute o app Android Fruitties e confira se ele funciona corretamente e usa o banco de dados do módulo :shared. Se você adicionou itens ao carrinho antes, eles também vão aparecer agora, mesmo que o banco de dados do Room esteja localizado no módulo :shared.
6. Preparar o Room para uso no iOS
Para preparar ainda mais o banco de dados do Room para a plataforma iOS, configure um código de suporte no módulo :shared para uso na próxima etapa.
Ativar a criação de banco de dados para o app iOS
A primeira coisa a fazer é adicionar um criador de banco de dados específico do iOS.
- Adicione um novo arquivo ao módulo
:sharedno conjunto de origemiosMainchamadoAppDatabase.ios.kt:
- Adicione as funções auxiliares a seguir. Elas serão usadas pelo app iOS para receber uma instância do banco de dados do Room.
package com.example.fruitties.kmptutorial.shared
import androidx.room.Room
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import com.example.fruitties.kmptutorial.android.database.AppDatabase
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.ObjCObjectVar
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
import platform.Foundation.NSDocumentDirectory
import platform.Foundation.NSError
import platform.Foundation.NSFileManager
import platform.Foundation.NSUserDomainMask
fun getPersistentDatabase(): AppDatabase {
val dbFilePath = documentDirectory() + "/" + "fruits.db"
return Room.databaseBuilder<AppDatabase>(name = dbFilePath)
.setDriver(BundledSQLiteDriver())
.build()
}
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
private fun documentDirectory(): String {
memScoped {
val errorPtr = alloc<ObjCObjectVar<NSError?>>()
val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
directory = NSDocumentDirectory,
inDomain = NSUserDomainMask,
appropriateForURL = null,
create = false,
error = errorPtr.ptr,
)
if (documentDirectory != null) {
return requireNotNull(documentDirectory.path) {
"""Couldn't determine the document directory.
URL $documentDirectory does not conform to RFC 1808.
""".trimIndent()
}
} else {
val error = errorPtr.value
val localizedDescription = error?.localizedDescription ?: "Unknown error occurred"
error("Couldn't determine document directory. Error: $localizedDescription")
}
}
}
Adicionar o sufixo "Entity" às entidades do Room
Como você está adicionando wrappers para as entidades do Room em Swift, é melhor que os nomes das entidades sejam diferentes dos nomes dos wrappers. Vamos garantir que esse seja o caso adicionando o sufixo Entity às entidades do Room usando a anotação @ObjCName.
Abra o arquivo Fruittie.kt no módulo :shared e adicione a anotação @ObjCName à entidade Fruittie. Como essa anotação é experimental, talvez seja necessário adicionar a @OptIn(ExperimentalObjC::class) ao arquivo.
Fruittie.kt
import kotlin.experimental.ExperimentalObjCName
import kotlin.native.ObjCName
@OptIn(ExperimentalObjCName::class)
@Entity(indices = [Index(value = ["id"], unique = true)])
@ObjCName("FruittieEntity")
data class Fruittie(
...
)
Agora, faça o mesmo para a entidade CartItem no arquivo CartItem.kt.
CartItem.kt
import kotlin.experimental.ExperimentalObjCName
import kotlin.native.ObjCName
@OptIn(ExperimentalObjCName::class)
@ObjCName("CartItemEntity")
@Entity(
foreignKeys = [
ForeignKey(
entity = Fruittie::class,
parentColumns = ["id"],
childColumns = ["id"],
onDelete = ForeignKey.CASCADE,
),
],
)
data class CartItem(@PrimaryKey val id: Long, val count: Int = 1)
7. Usar o Room no app iOS
O app iOS é um app já pronto que usa Core Data. Neste codelab, não é necessário migrar os dados atuais do banco de dados, já que este app é apenas um protótipo. Se você estiver migrando um app de produção para o KMP, vai precisar criar funções para ler o banco de dados Core Data atual e inserir esses itens no banco de dados do Room na primeira inicialização após a migração.
Abrir o projeto do Xcode.
Abra o projeto do iOS no Xcode navegando até a pasta /iosApp/ e abrindo Fruitties.xcodeproj no app associado.

Remover classes de entidades do Core Data
Primeiro, remova as classes de entidade do Core Data para abrir espaço para os wrappers de entidade que você vai criar depois. Dependendo do tipo de dados que o aplicativo armazena no Core Data, é possível remover as entidades completamente ou mantê-las para fins de migração de dados. Para este tutorial, basta removê-las, já que não é necessário migrar os dados atuais.
No Xcode:
- Acesse o Project Navigator.
- Acesse a pasta Resources.
- Abra o arquivo
Fruitties. - Clique em cada entidade e as exclua.

Para disponibilizar essas mudanças no código, limpe e recrie o projeto.
Isso vai fazer com que o build falhe com os erros abaixo, o que é esperado.

Como criar wrappers de entidade
Agora, vamos criar wrappers para as entidades Fruittie e CartItem e lidar com as diferenças de API entre as entidades do Room e do Core Data.
Esses wrappers vão ajudar na transição do Core Data para o Room, minimizando a quantidade de código que precisa ser atualizada imediatamente. O objetivo é substituir esses wrappers por acesso direto às entidades do Room no futuro.
Por enquanto, vamos criar um wrapper para a classe FruittieEntity, dando a ela propriedades opcionais.
Criar o wrapper de FruittieEntity
- Crie um novo arquivo Swift no diretório
Sources/Repositoryclicando com o botão direito do mouse no nome do diretório e selecionando New File from Template…

- Nomeie-o como
Fruittiee confira se apenas o destino "Fruitties" está selecionado, e não o destino de teste.
- Adicione o código abaixo ao novo arquivo Fruittie:
import sharedKit
struct Fruittie: Hashable {
let entity: FruittieEntity
var id: Int64 {
entity.id
}
var name: String? {
entity.name
}
var fullName: String? {
entity.fullName
}
}
O struct Fruittie encapsula a classe FruittieEntity, tornando as propriedades opcionais e transmitindo as propriedades da entidade. Além disso, fazemos com que o struct Fruittie obedeça ao protocolo Hashable para que ele possa ser usado na visualização ForEach da SwiftUI.
Criar o wrapper de CartItemEntity
Agora, crie um wrapper semelhante para a classe CartItemEntity.
Crie um arquivo Swift chamado CartItem.swift no diretório Sources/Repository.
import sharedKit
struct CartItem: Hashable {
let entity: CartItemWithFruittie
let fruittie: Fruittie?
var id: Int64 {
entity.cartItem.id
}
var count: Int64 {
Int64(entity.cartItem.count)
}
init(entity: CartItemWithFruittie) {
self.entity = entity
self.fruittie = Fruittie(entity: entity.fruittie)
}
}
Como a classe CartItem original do Core Data tinha uma propriedade Fruittie, também incluímos uma propriedade Fruittie no struct CartItem. Embora a classe CartItem não tenha propriedades opcionais, a propriedade count tem um tipo diferente na entidade do Room.
Atualizar repositórios
Agora que os wrappers de entidade estão prontos, você precisa atualizar o DefaultCartRepository e o DefaultFruittieRepository para usar o Room em vez do Core Data.
Atualizar DefaultCartRepository
Vamos começar com a classe DefaultCartRepository, já que ela é a mais simples das duas.
Abra o arquivo CartRepository.swift no diretório Sources/Repository.
- Primeiro, substitua a importação de
CoreDataporsharedKit:
import sharedKit
- Depois, remova a propriedade
NSManagedObjectContexte substitua por uma propriedadeCartDao:
// Remove
private let managedObjectContext: NSManagedObjectContext
// Replace with
private let cartDao: any CartDao
- Atualize o construtor
initpara inicializar a nova propriedadecartDao:
init(cartDao: any CartDao) {
self.cartDao = cartDao
}
- Em seguida, atualize o método
addToCart. Ele tinha que extrair os itens do carrinho do Core Data, mas nossa implementação do Room não exige isso. Em vez disso, ele vai inserir um novo item ou aumentar a contagem de um item do carrinho.
func addToCart(fruittie: Fruittie) async throws {
try await cartDao.insertOrIncreaseCount(fruittie: fruittie.entity)
}
- Por fim, atualize o método
getCartItems(). Ele vai chamar o métodogetAll()noCartDaoe mapear as entidadesCartItemWithFruittiepara nossos wrappersCartItem.
func getCartItems() -> AsyncStream<[CartItem]> {
return cartDao.getAll().map { entities in
entities.map(CartItem.init(entity:))
}.eraseToStream()
}
Atualizar DefaultFruittieRepository
Para migrar a classe DefaultFruittieRepository, aplicamos mudanças semelhantes às que fizemos para o DefaultCartRepository.
Atualize o arquivo FruittieRepository desta forma:
import ConcurrencyExtras
import sharedKit
protocol FruittieRepository {
func getData() -> AsyncStream<[Fruittie]>
}
class DefaultFruittieRepository: FruittieRepository {
private let fruittieDao: any FruittieDao
private let api: FruittieApi
init(fruittieDao: any FruittieDao, api: FruittieApi) {
self.fruittieDao = fruittieDao
self.api = api
}
func getData() -> AsyncStream<[Fruittie]> {
let dao = fruittieDao
Task {
let isEmpty = try await dao.count() == 0
if isEmpty {
let response = try await api.getData(pageNumber: 0)
let fruitties = response.feed.map {
FruittieEntity(
id: 0,
name: $0.name,
fullName: $0.fullName,
calories: ""
)
}
_ = try await dao.insert(fruitties: fruitties)
}
}
return dao.getAll().map { entities in
entities.map(Fruittie.init(entity:))
}.eraseToStream()
}
}
Substituir wrappers de propriedade @FetchRequest
Também precisamos substituir os wrappers de propriedade @FetchRequest nas visualizações da SwiftUI. O wrapper de propriedade @FetchRequest é usado para buscar dados do Core Data e observar mudanças. Ele não pode ser usado com entidades do Room. Em vez disso, vamos usar o UIModel para acessar os dados dos repositórios.
- Abra a
CartViewno arquivoSources/UI/CartView.swift. - Substitua a implementação pelo seguinte:
import SwiftUI
struct CartView : View {
@State
private var expanded = false
@ObservedObject
private(set) var uiModel: ContentViewModel
var body: some View {
if (uiModel.cartItems.isEmpty) {
Text("Cart is empty, add some items").padding()
} else {
HStack {
Text("Cart has \(uiModel.cartItems.count) items (\(uiModel.cartItems.reduce(0) { $0 + $1.count }))")
.padding()
Spacer()
Button {
expanded.toggle()
} label: {
if (expanded) {
Text("collapse")
} else {
Text("expand")
}
}
.padding()
}
if (expanded) {
VStack {
ForEach(uiModel.cartItems, id: \.self) { item in
Text("\(item.fruittie!.name!): \(item.count)")
}
}
}
}
}
}
Atualizar a ContentView
Atualize a ContentView no arquivo Sources/View/ContentView.swift para transmitir o FruittieUIModel à CartView.
CartView(uiModel: uiModel)
Atualizar o DataController
A classe DataController no aplicativo iOS é responsável por configurar a pilha do Core Data. Como estamos migrando do Core Data, precisamos atualizar o DataController para inicializar o banco de dados do Room.
- Abra o arquivo
DataController.swiftemSources/Database. - Adicione a importação
sharedKit. - Remova a importação
CoreData. - Crie uma instância do banco de dados do Room na classe
DataController. - Por fim, remova a chamada do método
loadPersistentStoresdo inicializadorDataController.
A classe atualizada ficará assim:
import Combine
import sharedKit
class DataController: ObservableObject {
let database = getPersistentDatabase()
init() {}
}
Como atualizar a injeção de dependência
A classe AppContainer no aplicativo iOS é responsável por inicializar o gráfico de dependências. Como atualizamos os repositórios para usar o Room em vez do Core Data, precisamos atualizar o AppContainer para transmitir DAOs do Room aos repositórios.
- Abra o arquivo
AppContainer.swiftna pastaSources/DI. - Adicione a importação
sharedKit. - Remova a propriedade
managedObjectContextda classeAppContainer. - Mude as inicializações
DefaultFruittieRepositoryeDefaultCartRepositorytransmitindo DAOs do Room da instânciaAppDatabasefornecida peloDataController.
Quando terminar, a classe ficará assim:
import Combine
import Foundation
import sharedKit
class AppContainer: ObservableObject {
let dataController: DataController
let api: FruittieApi
let fruittieRepository: FruittieRepository
let cartRepository: CartRepository
init() {
dataController = DataController()
api = FruittieNetworkApi(
apiUrl: URL(
string:
"https://android.github.io/kotlin-multiplatform-samples/fruitties-api"
)!)
fruittieRepository = DefaultFruittieRepository(
fruittieDao: dataController.database.fruittieDao(),
api: api
)
cartRepository = DefaultCartRepository(
cartDao: dataController.database.cartDao()
)
}
}
Por fim, atualize o FruittiesApp no arquivo main.swift para remover o managedObjectContext:
struct FruittiesApp: App {
@StateObject
private var appContainer = AppContainer()
var body: some Scene {
WindowGroup {
ContentView(appContainer: appContainer)
}
}
}
Criar e executar o app iOS
Por fim, depois de criar e executar o app pressionando ⌘R, ele vai começar com o banco de dados migrado do Core Data para o Room.

8. Parabéns
Parabéns! Você migrou apps Android e iOS independentes para uma camada de dados compartilhada usando o KMP do Room.
Para referência, confira a comparação das arquiteturas de apps para ver o que fizemos:
Antes
Android | iOS |
|
|
Após a migração para Android

Saiba mais
- Saiba quais outras bibliotecas do Jetpack são compatíveis com o KMP
- Leia a documentação do Room KMP.
- Leia a documentação do SQLite KMP.
- Consulte a documentação oficial do Kotlin Multiplatform.