1. Antes de comenzar
Requisitos previos
- Conocimientos básicos sobre Kotlin Multiplataforma
- Experiencia con Kotlin
- Conocimientos básicos de la sintaxis de Swift
- Xcode y el simulador de iOS instalados
Requisitos
- La versión estable más reciente de Android Studio
- Una máquina Mac con sistema macOS
- Xcode 16.1 y el simulador de iPhone con iOS 16.0 o una versión posterior
Qué aprenderás
- Cómo compartir una base de datos de Room entre una app para Android y una para iOS
2. Prepárate
Para comenzar, sigue estos pasos:
- Clona el repositorio de GitHub con el siguiente comando de la terminal:
$ git clone https://github.com/android/codelab-android-kmp.git
También tienes la opción de descargar el repositorio como archivo ZIP:
- En Android Studio, abre el proyecto
migrate-room
, que contiene las siguientes ramas:
main
: contiene el código de partida para este proyecto, en el que realizarás cambios para completar el codelab.end
: contiene el código de solución para este codelab.
Te recomendamos que comiences con la rama main
y sigas el codelab paso a paso a tu propio ritmo.
- Si quieres ver el código de solución, ejecuta este comando:
$ git clone -b end https://github.com/android/codelab-android-kmp.git
También puedes descargar el código de solución:
3. Comprende la app de ejemplo
Este instructivo consta de la aplicación de ejemplo Fruitties compilada en frameworks nativos (Jetpack Compose en Android y SwiftUI en iOS).
La app de Fruitties ofrece dos funciones principales:
- Una lista de elementos Fruta, cada uno con un botón para agregar el elemento a un carrito.
- Un carrito que se muestra en la parte superior, en el que se indica cuántas frutas se agregaron y sus cantidades.
Arquitectura de apps para Android
La app para Android sigue los lineamientos oficiales de arquitectura de Android para mantener una estructura clara y modular.
Arquitectura de la app para iOS
Módulo compartido de KMP
Este proyecto ya se configuró con un módulo compartido para KMP, aunque actualmente está vacío. Si aún no configuraste tu proyecto con un módulo compartido, comienza con el codelab Introducción a Kotlin Multiplataforma.
4. Prepara la base de datos de Room para la integración de KMP
Antes de mover el código de la base de datos de Room de la app para Android Fruitties al módulo shared
, debes asegurarte de que la app sea compatible con las APIs de Room de Kotlin Multiplataforma (KMP). En esta sección, se explica ese proceso.
Una actualización clave es usar un controlador SQLite compatible con Android y iOS. Para admitir la funcionalidad de la base de datos de Room en varias plataformas, puedes usar BundledSQLiteDriver
. Este controlador empaqueta SQLite directamente en la aplicación, lo que lo hace adecuado para el uso multiplataforma en Kotlin. Para obtener ayuda detallada, consulta la guía de migración de Room en KMP.
Actualiza las dependencias
Primero, agrega las dependencias room-runtime
y sqlite-bundled
al archivo 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" }
Luego, actualiza el objeto build.gradle.kts
del módulo :androidApp
para usar estas dependencias y quita el uso de libs.androidx.room.ktx
:
// Add
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
// Remove
implementation(libs.androidx.room.ktx)
Ahora, sincroniza el proyecto en Android Studio.
Modifica el módulo de base de datos para BundledSQLiteDriver
A continuación, modifica la lógica de creación de la base de datos en la app para Android para usar BundledSQLiteDriver
, lo que la hará compatible con KMP y, al mismo tiempo, mantendrá la funcionalidad en Android.
- Abre el archivo
DatabaseModule.kt
ubicado enandroidApp/src/main/kotlin/com/example/fruitties/kmptutorial/android/di/DatabaseModule.kt
. - Actualiza el método
providesAppDatabase
como en el siguiente fragmento:
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()
}
Compila y ejecuta la app para Android
Ahora que cambiaste el controlador SQLite nativo al incluido en el paquete, verifiquemos que la app se compile y todo funcione correctamente antes de migrar la base de datos al módulo :shared
.
5. Mueve el código de la base de datos al módulo :shared
En este paso, transferiremos la configuración de la base de datos de Room de la app para Android al módulo :shared
, lo que permitirá que Android y iOS puedan acceder a la base de datos.
Actualiza la configuración de build.gradle.kts
del módulo :shared
.
Comienza por actualizar el build.gradle.kts
del módulo :shared
para usar las dependencias multiplataforma de Room.
- Agrega los complementos KSP y Room:
plugins {
...
// TODO add KSP + ROOM plugins
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
- Agrega las dependencias
room-runtime
ysqlite-bundled
al bloquecommonMain
:
sourceSets {
commonMain {
// TODO Add KMP dependencies here
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
}
}
- Agrega un nuevo bloque
dependencies
de nivel superior para agregar la configuración de KSP en cada objetivo de plataforma. Para facilitar el proceso, puedes agregarlo al final del archivo:
// 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)
}
- También en el nivel superior, agrega un nuevo bloque para establecer la ubicación del esquema
Room
:
// Should be its own top level block. For convenience, add at the bottom of the file
room {
schemaDirectory("$projectDir/schemas")
}
- Sincroniza el proyecto con Gradle
Mueve el esquema de Room al módulo :shared
Mueve el directorio androidApp/schemas
a la carpeta raíz del módulo :shared
junto a la carpeta src/
:
De:
Para:
Mueve DAO y entidades
Ahora que agregaste las dependencias de Gradle necesarias al módulo compartido de KMP, debes mover las DAO y las entidades del módulo :androidApp
al módulo :shared
.
Esto implicará mover archivos a sus ubicaciones respectivas en el conjunto de orígenes commonMain
del módulo :shared
.
Mueve el modelo Fruittie
Puedes aprovechar la funcionalidad Refactor → Move para cambiar de módulo sin interrumpir las importaciones:
- Busca el archivo
androidApp/src/main/kotlin/.../model/Fruittie.kt
, haz clic con el botón derecho en él y selecciona Refactor → Move (o la tecla F6): - En el diálogo Move, selecciona el ícono
...
junto al campo Destination directory. - Selecciona el conjunto de orígenes commonMain en el diálogo Choose Destination Directory y haz clic en Ok. Es posible que debas inhabilitar la casilla de verificación Show only existing source roots.
- Haz clic en el botón Refactor para mover el archivo.
Mueve los modelos CartItem
y CartItemWithFruittie
Para el archivo androidApp/.../model/CartItem.kt
, debes seguir estos pasos:
- Abre el archivo, haz clic con el botón derecho en la clase
CartItem
y selecciona Refactor > Move. - Se abrirá el mismo diálogo Move. Sin embargo, en este caso, también debes marcar la casilla del miembro
CartItemWithFruittie
. Para continuar, selecciona el ícono
...
y el conjunto de orígenescommonMain
, tal como lo hiciste con el archivoFruittie.kt
.
Mueve DAO y AppDatabase
Repite los mismos pasos para los siguientes archivos (puedes seleccionar los tres al mismo tiempo):
androidApp/.../database/FruittieDao.kt
androidApp/.../database/CartDao.kt
androidApp/.../database/AppDatabase.kt
Actualiza el AppDatabase
compartido para que funcione en varias plataformas
Ahora que moviste las clases de la base de datos al módulo :shared
, debes ajustarlas para generar las implementaciones requeridas en ambas plataformas.
- Abre el archivo
/shared/src/commonMain/kotlin/com/example/fruitties/kmptutorial/android/database/AppDatabase.kt
. - Agrega la siguiente implementación 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
}
- Anota la clase
AppDatabase
con@ConstructedBy(AppDatabaseConstructor::class)
:
import androidx.room.ConstructedBy
@Database(
entities = [Fruittie::class, CartItem::class],
version = 1,
)
// TODO Add this line
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
...
Mueve la creación de bases de datos al módulo :shared
Luego, moverás la configuración de Room específica para Android del módulo :androidApp
al módulo :shared
. Es necesario realizar esto porque, en el siguiente paso, quitarás la dependencia de Room
del módulo :androidApp
.
- Ubica el archivo
androidApp/.../di/DatabaseModule.kt
. - Selecciona el contenido de la función
providesAppDatabase
, haz clic con el botón derecho y selecciona Refactor > Extract Function to Scope: - Selecciona
DatabaseModule.kt
en el menú.Esto mueve el contenido a una función
appDatabase
global. Presiona Intro para confirmar el nombre de la función. - Para que la función sea pública, quita el modificador de visibilidad
private
. - Para mover la función al módulo
:shared
, haz clic con el botón derecho en Refactor > Move. - En el cuadro de diálogo Move, selecciona el ícono … junto al campo Destination directory.
- En el cuadro de diálogo Choose Destination Directory, selecciona el conjunto de orígenes shared >androidMain y la carpeta /shared/src/androidMain/. Luego, haz clic en OK.
- Cambia el sufijo en el campo To package de
.di
a.database
.
- Haz clic en Refactor.
Limpia el código innecesario de :androidApp
En este punto, moviste la base de datos de Room al módulo multiplataforma y no se necesita ninguna de las dependencias de Room en el módulo :androidApp
, por lo que puedes quitarlas.
- Abre el archivo
build.gradle.kts
en el módulo:androidApp
. - Quita las dependencias y la configuración como en el siguiente fragmento:
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")
}
- Sincroniza el proyecto con Gradle.
Compila y ejecuta la app para Android
Ejecuta la app para Android Fruitties para asegurarte de que se funciona correctamente y ahora use la base de datos del módulo :shared
. Si agregaste artículos al carrito antes, deberías ver los mismos artículos en este punto, aunque la base de datos de Room ahora se encuentra en el módulo :shared
.
6. Prepara Room para su uso en iOS
Para preparar aún más la base de datos de Room para la plataforma iOS, debes configurar un código de compatibilidad en el módulo :shared
para usarlo en el siguiente paso.
Habilita la creación de bases de datos para la app para iOS
Lo primero que debes hacer es agregar un compilador de bases de datos específico para iOS.
- Agrega un archivo nuevo en el módulo
:shared
del conjunto de orígenesiosMain
llamadoAppDatabase.ios.kt
: - Agrega las siguientes funciones auxiliares. La app para iOS usará estas funciones para obtener una instancia de la base de datos de 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")
}
}
}
Agrega el sufijo "Entity" a las entidades de Room
Como agregas wrappers para las entidades de Room en Swift, es mejor que los nombres de las entidades de Room difieran de los nombres de los wrappers. Para asegurarnos de que sea así, agregaremos el sufijo Entity a las entidades de Room con la anotación @ObjCName
.
Abre el archivo Fruittie.kt
en el módulo :shared
y agrega la anotación @ObjCName
a la entidad Fruittie
. Como esta anotación es experimental, es posible que debas agregar la anotación @OptIn(ExperimentalObjC::class)
al archivo.
Fruittie.kt
import kotlin.experimental.ExperimentalObjCName
import kotlin.native.ObjCName
@OptIn(ExperimentalObjCName::class)
@Serializable
@Entity(indices = [Index(value = ["id"], unique = true)])
@ObjCName("FruittieEntity")
data class Fruittie(
...
)
Luego, haz lo mismo con la entidad CartItem
en el archivo 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. Usa Room en la app para iOS
La app para iOS es una app preexistente que usa Core Data. En este codelab, no tendrás que preocuparte por migrar los datos existentes en la base de datos, ya que esta app es solo un prototipo. Si migras una app de producción a KMP, tendrás que escribir funciones para leer la base de datos actual de Core Data y, luego, insertar esos elementos en la base de datos de Room en el primer inicio después de la migración.
Abre el proyecto de Xcode
Para abrir el proyecto de iOS en Xcode, navega a la carpeta /iosApp/
y abre Fruitties.xcodeproj
en la app asociada.
Quita las clases de entidades de Core Data
Primero, debes quitar las clases de entidades de Core Data para dejar espacio para los wrappers de entidades que crearás más adelante. Según el tipo de datos que almacene tu aplicación en Core Data, puedes quitar las entidades de Core Data por completo o conservarlas para la migración de datos. Para este instructivo, puedes quitarlas, ya que no necesitarás migrar ningún dato existente.
En Xcode:
- Ve a Project Navigator.
- Ve a la carpeta Resources.
- Abre el archivo
Fruitties
. - Haz clic en cada entidad y bórralas.
Para que estos cambios estén disponibles en el código, limpia y vuelve a compilar el proyecto.
Esto hará que la compilación falle con los siguientes errores, lo cual es esperable.
Cómo crear wrappers de entidades
A continuación, crearemos wrappers de entidades para las entidades Fruittie
y CartItem
para controlar de manera fluida las diferencias de API entre las entidades de Room y Core Data.
Estos wrappers nos ayudarán a realizar la transición de Core Data a Room, ya que minimizan la cantidad de código que se debe actualizar de inmediato. En el futuro, debes reemplazar estos wrappers por accesos directos a las entidades de Room.
Por ahora, en su lugar, crearemos un wrapper para la clase FruittieEntity
y le daremos propiedades opcionales.
Crea un wrapper FruittieEntity
- Para crear un archivo Swift nuevo en el directorio
Sources/Repository
, haz clic con el botón derecho en el nombre del directorio y selecciona New File from Template… - Nómbralo
Fruittie
y asegúrate de que solo se seleccione el objetivo de Fruitties, no el objetivo de prueba. - Agrega el siguiente código al nuevo archivo de Fruittie:
import sharedKit
struct Fruittie: Hashable {
let entity: FruittieEntity
var id: Int64 {
entity.id
}
var name: String? {
entity.name
}
var fullName: String? {
entity.fullName
}
}
El struct Fruittie
une la clase FruittieEntity
, lo que hace que las propiedades sean opcionales y pase a través de las propiedades de la entidad. Además, hacemos que el struct Fruittie
cumpla con el protocolo Hashable
, de modo que se pueda usar en la vista ForEach
de SwiftUI.
Crea un wrapper CartItemEntity
A continuación, crea un wrapper similar para la clase CartItemEntity
.
Crea un archivo Swift nuevo llamado CartItem.swift
en el directorio 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)
}
}
Dado que la clase CartItem
original de Core Data tenía una propiedad Fruittie
, también incluimos una propiedad Fruittie
en el struct CartItem
. Si bien la clase CartItem
no tiene ninguna propiedad opcional, la propiedad count
tiene un tipo diferente en la entidad Room.
Actualiza los repositorios
Ahora que los wrappers de entidades están en su lugar, debes actualizar DefaultCartRepository
y DefaultFruittieRepository
para usar Room en lugar de Core Data.
Actualiza DefaultCartRepository
Comencemos con la clase DefaultCartRepository
, ya que es la más simple de las dos.
Abre el archivo CartRepository.swift
en el directorio Sources/Repository
.
- Primero, reemplaza la importación de
CoreData
porsharedKit
:
import sharedKit
- Luego, quita la propiedad
NSManagedObjectContext
y reemplázala por una propiedadCartDao
:
// Remove
private let managedObjectContext: NSManagedObjectContext
// Replace with
private let cartDao: any CartDao
- Actualiza el constructor de
init
para inicializar la nueva propiedadcartDao
:
init(cartDao: any CartDao) {
self.cartDao = cartDao
}
- A continuación, actualiza el método
addToCart
. Este método necesitaba extraer elementos del carrito existentes de Core Data, pero nuestra implementación de Room no lo requiere. En su lugar, insertará un elemento nuevo o incrementará el recuento de un elemento del carrito existente.
func addToCart(fruittie: Fruittie) async throws {
try await cartDao.insertOrIncreaseCount(fruittie: fruittie.entity)
}
- Por último, actualiza el método
getCartItems()
. Este método llamará al métodogetAll()
enCartDao
y asignará las entidadesCartItemWithFruittie
a nuestros wrappersCartItem
.
func getCartItems() -> AsyncStream<[CartItem]> {
return cartDao.getAll().map { entities in
entities.map(CartItem.init(entity:))
}.eraseToStream()
}
Actualiza DefaultFruittieRepository
Para migrar la clase DefaultFruittieRepository
, aplicamos cambios similares a los que hicimos para DefaultCartRepository
.
Actualiza el archivo FruittieRepository a lo siguiente:
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()
}
}
Reemplaza los wrappers de propiedades @FetchRequest
También debemos reemplazar los wrappers de propiedades @FetchRequest
en las vistas de SwiftUI. El wrapper de propiedades @FetchRequest
se usa para recuperar datos de Core Data y observar cambios, por lo que no podemos usarlo con entidades de Room. En su lugar, usemos UIModel para acceder a los datos de los repositorios.
- Abre el
CartView
en el archivoSources/UI/CartView.swift
. - Reemplaza la implementación por lo siguiente:
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)")
}
}
}
}
}
}
Actualiza ContentView
Actualiza ContentView
en el archivo Sources/View/ContentView.swift
para pasar FruittieUIModel
a CartView
.
CartView(uiModel: uiModel)
Cómo actualizar DataController
La clase DataController
en la aplicación para iOS es responsable de configurar la pila de Core Data. Dado que nos alejaremos de Core Data, debemos actualizar el DataController
para inicializar la base de datos de Room.
- Abre el archivo
DataController.swift
enSources/Database
. - Agrega la importación de
sharedKit
. - Quita la importación de
CoreData
. - Crea una instancia de la base de datos de Room en la clase
DataController
. - Por último, quita la llamada al método
loadPersistentStores
del inicializadorDataController
.
La clase actualizada debería verse de la siguiente manera:
import Combine
import sharedKit
class DataController: ObservableObject {
let database = getPersistentDatabase()
init() {}
}
Actualiza la inserción de dependencias
La clase AppContainer
en la aplicación para iOS es responsable de inicializar el gráfico de dependencias. Dado que actualizamos los repositorios para usar Room en lugar de Core Data, debemos actualizar AppContainer
para pasar los DAO de Room a los repositorios.
- Abre
AppContainer.swift
en la carpetaSources/DI
. - Agrega la importación de
sharedKit
. - Quita la propiedad
managedObjectContext
de la claseAppContainer
. - Para cambiar las inicializaciones de
DefaultFruittieRepository
yDefaultCartRepository
, pasa los DAO de Room de la instanciaAppDatabase
que proporcionaDataController
.
Cuando termines, la clase se verá de la siguiente manera:
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 último, actualiza FruittiesApp
en main.swift
para quitar managedObjectContext
:
struct FruittiesApp: App {
@StateObject
private var appContainer = AppContainer()
var body: some Scene {
WindowGroup {
ContentView(appContainer: appContainer)
}
}
}
Compila y ejecuta la app para iOS
Por último, una vez que compiles y ejecutes la app presionando ⌘R, esta debería iniciarse con la base de datos migrada de Core Data a Room.
8. Felicitaciones
¡Felicitaciones! Migraste correctamente apps independientes para iOS y Android a una capa de datos compartida con Room en KMP.
A modo de referencia, esta es la comparación de las arquitecturas de la app para ver lo que se logró:
Antes
Android | iOS |
Arquitectura posterior a la migración
Más información
- Obtén información sobre qué otras bibliotecas de Jetpack admiten KMP.
- Lee la documentación de Room en KMP.
- Lee la documentación de SQLite en KMP.
- Consulta la documentación oficial de Kotlin Multiplataforma.