1. Avant de commencer
Conditions préalables
- Vous avez des connaissances de base en Kotlin Multiplatform.
- Vous maîtrisez le langage Kotlin.
- Vous disposez de connaissances de base concernant la syntaxe Swift.
- Xcode et le simulateur iOS sont installés.
Ce dont vous avez besoin
- La dernière version stable d'Android Studio
- Un ordinateur Mac avec un système macOS
- Xcode 16.1 et simulateur iPhone avec iOS 16.0 ou version ultérieure
Objectifs de l'atelier
- Comment partager une base de données Room entre une application Android et une application iOS
2. Configuration
Pour commencer, procédez comme suit :
- Clonez le dépôt GitHub à l'aide de la commande de terminal suivante:
$ git clone https://github.com/android/codelab-android-kmp.git
Vous pouvez également télécharger le dépôt sous forme de fichier ZIP :
- Dans Android Studio, ouvrez le projet
migrate-room
, qui contient les branches suivantes:
main
: contient le code de démarrage de ce projet, que vous allez modifier tout au long de l'atelier.end
: contient le code de solution pour cet atelier.
Nous vous recommandons de commencer avec la branche main
et de suivre l'atelier de programmation étape par étape, à votre rythme.
- Si vous souhaitez voir le code de solution, exécutez la commande suivante :
$ git clone -b end https://github.com/android/codelab-android-kmp.git
Vous pouvez également télécharger le code de solution :
3. Comprendre l'application exemple
Ce tutoriel consiste en l'application exemple Fruitties, créée dans des frameworks natifs (Jetpack Compose sur Android, SwiftUi sur iOS).
L'application Fruitties offre deux fonctionnalités principales:
- Une liste d'éléments Fruit, chacun avec un bouton permettant d'ajouter l'élément à un panier.
- Un panier s'affiche en haut de l'écran. Il indique le nombre de fruits ajoutés et leur quantité.
Architecture des applications Android
L'application Android suit les Consignes officielles concernant l'architecture Android pour conserver une structure claire et modulaire.
Architecture de l'application iOS
Module partagé KMP
Ce projet a déjà été configuré avec un module partagé pour KMP, mais il est actuellement vide. Si votre projet n'a pas encore été configuré avec un module partagé, commencez par l'atelier de programmation Premiers pas avec Kotlin Multiplatform.
4. Préparer la base de données Room pour l'intégration de KMP
Avant de déplacer le code de la base de données Room de l'application Android Fruitties vers le module shared
, vous devez vous assurer que l'application est compatible avec les API Room de Kotlin Multiplatform (KMP). Cette section vous guide tout au long de ce processus.
L'une des principales mises à jour consiste à utiliser un pilote SQLite compatible avec Android et iOS. Pour prendre en charge la fonctionnalité de la base de données Room sur plusieurs plates-formes, vous pouvez utiliser BundledSQLiteDriver
. Ce pilote regroupe SQLite directement dans l'application, ce qui le rend adapté à une utilisation multiplate-forme en Kotlin. Pour obtenir des conseils détaillés, consultez le guide de migration de Room KMP.
Mettre à jour les dépendances
Commencez par ajouter les dépendances room-runtime
et sqlite-bundled
au fichier 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" }
Ensuite, mettez à jour le build.gradle.kts
du module :androidApp
pour utiliser ces dépendances et supprimez l'utilisation de libs.androidx.room.ktx
:
// Add
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
// Remove
implementation(libs.androidx.room.ktx)
Synchronisez maintenant le projet dans Android Studio.
Modifier le module de base de données pour BundledSQLiteDriver
Modifiez ensuite la logique de création de la base de données dans l'application Android pour utiliser le BundledSQLiteDriver
, ce qui la rend compatible avec le langage KMP tout en conservant sa fonctionnalité sur Android.
- Ouvrez le fichier
DatabaseModule.kt
qui se trouve sousandroidApp/src/main/kotlin/com/example/fruitties/kmptutorial/android/di/DatabaseModule.kt
. - Mettez à jour la méthode
providesAppDatabase
comme dans l'extrait de code suivant :
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()
}
Compiler et exécuter l'application Android
Maintenant que vous avez remplacé le pilote SQLite natif par le pilote fourni, vérifions que l'application est compilée et que tout fonctionne correctement avant de migrer la base de données vers le module :shared
.
5. Déplacer le code de la base de données vers le module :shared
Au cours de cette étape, nous allons transférer la configuration de la base de données Room de l'application Android vers le module :shared
, ce qui permettra à la base de données d'être accessible à la fois sur Android et sur iOS.
Mettre à jour la configuration build.gradle.kts
du module :shared
Commencez par mettre à jour la configuration build.gradle.kts
du module :shared
pour utiliser les dépendances multiplates-formes Room.
- Ajoutez les plug-ins KSP et Room:
plugins {
...
// TODO add KSP + ROOM plugins
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
- Ajoutez les dépendances
room-runtime
etsqlite-bundled
au bloccommonMain
:
sourceSets {
commonMain {
// TODO Add KMP dependencies here
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
}
}
- Ajoutez une configuration KSP pour chaque plate-forme cible en ajoutant un nouveau bloc
dependencies
de premier niveau. Pour plus de simplicité, vous pouvez l'ajouter en bas du fichier :
// 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)
}
- Toujours au premier niveau, ajoutez un bloc pour définir l'emplacement du schéma
Room
:
// Should be its own top level block. For convenience, add at the bottom of the file
room {
schemaDirectory("$projectDir/schemas")
}
- Gradle synchronise le projet
Déplacer le schéma Room vers le module :shared
Déplacez le répertoire androidApp/schemas
vers le dossier racine du module :shared
, à côté du dossier src/
:
De :
À :
Déplacer des DAO et des entités
Maintenant que vous avez ajouté les dépendances Gradle nécessaires au module partagé KMP, vous devez déplacer les DAO et les entités du module :androidApp
vers le module :shared
.
Pour ce faire, vous devrez déplacer les fichiers vers leurs emplacements respectifs dans l'ensemble de sources commonMain
du module :shared
.
Déplacer le modèle Fruittie
Vous pouvez utiliser la fonctionnalité Refactoriser → Déplacer pour changer de module sans interrompre les importations :
- Trouvez le fichier
androidApp/src/main/kotlin/.../model/Fruittie.kt
, effectuez un clic droit dessus, puis sélectionnez Refactoriser → Déplacer (ou appuyez sur la touche F6) : - Dans la boîte de dialogue Move (Déplacer), sélectionnez l'icône
...
à côté du champ Destination directory (Répertoire de destination). - Sélectionnez l'ensemble de sources commonMain dans la boîte de dialogue Choose Destination Directory (Sélectionner le répertoire de destination), puis cliquez sur OK. Vous devrez peut-être désactiver la case à cocher Show only existing source roots (Afficher uniquement les racines de source existantes).
- Cliquez sur le bouton Refactor (Refactoriser) pour déplacer le fichier.
Déplacer les modèles CartItem
et CartItemWithFruittie
Pour le fichier androidApp/.../model/CartItem.kt
, procédez comme suit:
- Ouvrez le fichier, effectuez un clic droit sur la classe
CartItem
, puis sélectionnez Refactor > Move (Refactoriser > Déplacer). - La même boîte de dialogue Move (Déplacer) s'ouvre, mais dans ce cas, vous cochez également la case du membre
CartItemWithFruittie
. Pour continuer, sélectionnez l'icône
...
, puis l'ensemble de sourcescommonMain
, comme vous l'avez fait pour le fichierFruittie.kt
.
Déplacer les DAO et AppDatabase
Suivez la même procédure pour les fichiers suivants (vous pouvez les sélectionner tous les trois en même temps) :
androidApp/.../database/FruittieDao.kt
androidApp/.../database/CartDao.kt
androidApp/.../database/AppDatabase.kt
Mettre à jour le AppDatabase
partagé pour qu'il fonctionne sur plusieurs plates-formes
Maintenant que les classes de base de données ont été déplacées vers le module :shared
, vous devez les ajuster pour générer les implémentations requises sur les deux plates-formes.
- Ouvrez le fichier
/shared/src/commonMain/kotlin/com/example/fruitties/kmptutorial/android/database/AppDatabase.kt
. - Ajoutez l'implémentation suivante 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
}
- Annotez la classe
AppDatabase
avec@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() {
...
Déplacer la création de la base de données vers le module :shared
Ensuite, déplacez la configuration Room spécifique à Android du module :androidApp
vers le module :shared
. Cette étape est nécessaire, car à l'étape suivante, vous allez supprimer la dépendance Room
du module :androidApp
.
- Trouvez le fichier
androidApp/.../di/DatabaseModule.kt
. - Sélectionnez le contenu de la fonction
providesAppDatabase
, effectuez un clic droit, puis sélectionnez Refactor > Extract Function to Scope (Refactoriser > Extraire la fonction dans le champ d'application) : - Sélectionnez
DatabaseModule.kt
dans le menu.: le contenu est déplacé vers une fonction
appDatabase
globale. Appuyez sur Enter (Entrée) pour confirmer le nom de la fonction. - Rendez la fonction publique en supprimant le modificateur de visibilité
private
. - Déplacez la fonction dans le module
:shared
en effectuant un clic droit sur Refactor > Move (Refactoriser > Déplacer). - Dans la boîte de dialogue Move (Déplacer), sélectionnez l'icône … située à côté du champ Destination directory (Répertoire de destination).
- Dans la boîte de dialogue Choose Destination Directory (Sélectionner le répertoire de destination), sélectionnez l'ensemble de sources shared > androidMain, puis le dossier /shared/src/androidMain/. Cliquez ensuite sur OK.
- Remplacez le suffixe
.di
par.database
dans le champ To package.
- Cliquez sur Refactor (Refactoriser).
Nettoyer le code inutile de :androidApp
À ce stade, vous avez déplacé la base de données Room vers le module multiplate-forme. Aucune des dépendances Room n'est nécessaire dans le module :androidApp
. Vous pouvez donc les supprimer.
- Ouvrez le fichier
build.gradle.kts
dans le module:androidApp
. - Supprimez les dépendances et la configuration comme dans l'extrait de code suivant :
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")
}
- Gradle synchronise le projet.
Compiler et exécuter l'application Android
Exécutez l'application Android Fruitties pour vous assurer qu'elle fonctionne correctement et qu'elle utilise désormais la base de données du module :shared
. Si vous aviez déjà ajouté des articles au panier, vous devriez voir les mêmes articles à ce stade, même si la base de données Room se trouve désormais dans le module :shared
.
6. Préparer Room pour une utilisation sur iOS
Pour préparer la base de données Room pour la plate-forme iOS, vous devez configurer un code de compatibilité dans le module :shared
pour l'utiliser à l'étape suivante.
Permettre la création de bases de données pour l'application iOS
La première chose à faire est d'ajouter un compilateur de base de données spécifique à iOS.
- Ajoutez un fichier dans le module
:shared
de l'ensemble de sourcesiosMain
nomméAppDatabase.ios.kt
: - Ajoutez les fonctions d'assistance suivantes. Ces fonctions seront utilisées par l'application iOS pour obtenir une instance de la base de données 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")
}
}
}
Ajout du suffixe "Entity" aux entités Room
Comme vous ajoutez des wrappers pour les entités Room dans Swift, il est préférable que les noms des entités Room soient différents de ceux des wrappers. Pour nous en assurer, nous allons ajouter le suffixe Entity aux entités Room à l'aide de l'annotation @ObjCName
.
Ouvrez le fichier Fruittie.kt
dans le module :shared
et ajoutez l'annotation @ObjCName
à l'entité Fruittie
. Comme cette annotation est expérimentale, vous devrez peut-être ajouter l'annotation @OptIn(ExperimentalObjC::class)
au fichier.
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(
...
)
Ensuite, procédez de la même manière pour l'entité CartItem
dans le fichier 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. Utiliser Room dans l'application iOS
L'application iOS est une application préexistante qui utilise Core Data. Dans cet atelier de programmation, vous n'aurez pas à vous soucier de la migration des données existantes dans la base de données, car cette application n'est qu'un prototype. Si vous migrez une application de production vers KMP, vous devrez écrire des fonctions pour lire la base de données Core Data actuelle et insérer ces éléments dans la base de données Room lors du premier lancement après la migration.
Ouvrir le projet Xcode
Ouvrez le projet iOS dans Xcode en accédant au dossier /iosApp/
et en ouvrant Fruitties.xcodeproj
dans l'application associée.
Supprimer les classes d'entités Core Data
Tout d'abord, vous devez supprimer les classes d'entités Core Data pour laisser de la place aux wrappers d'entités que vous créerez plus tard. Selon le type de données stockées par votre application dans Core Data, vous pouvez supprimer complètement les entités Core Data ou les conserver à des fins de migration des données. Pour ce tutoriel, vous pouvez simplement les supprimer, car vous n'aurez pas besoin de migrer les données existantes.
Dans Xcode :
- Accédez au navigateur de projet.
- Accédez au dossier "Resources".
- Ouvrez le fichier
Fruitties
. - Cliquez sur chaque entité et supprimez-les.
Pour rendre ces modifications disponibles dans le code, nettoyez et recompilez le projet.
La compilation échoue et les erreurs suivantes s'affichent, ce qui est normal.
Créer des wrappers d'entités
Nous allons ensuite créer des wrappers d'entités pour les entités Fruittie
et CartItem
afin de gérer facilement les différences d'API entre les entités Room et Core Data.
Ces wrappers nous aideront à passer de Core Data à Room en réduisant la quantité de code à mettre à jour immédiatement. À l'avenir, vous devrez remplacer ces wrappers par un accès direct aux entités Room.
Pour l'instant, nous allons créer un wrapper pour la classe FruittieEntity
, en lui attribuant des propriétés facultatives.
Créer un wrapper FruittieEntity
- Créez un nouveau fichier Swift dans le répertoire
Sources/Repository
en effectuant un clic droit sur le nom du répertoire, puis en sélectionnant New File from Template… (Nouveau fichier à partir du modèle…) - Nommez-le
Fruittie
et assurez-vous que seule la cible Fruitties est sélectionnée, et non la cible de test. - Ajoutez le code suivant au nouveau fichier Fruittie:
import sharedKit
struct Fruittie: Hashable {
let entity: FruittieEntity
var id: Int64 {
entity.id
}
var name: String? {
entity.name
}
var fullName: String? {
entity.fullName
}
}
Le struct Fruittie
encapsule la classe FruittieEntity
, ce qui rend les propriétés facultatives et les transmet à aux propriétés de l'entité. De plus, nous rendons le struct Fruittie
conforme au protocole Hashable
afin qu'il puisse être utilisé dans l'affichage ForEach
de SwiftUI.
Créer un wrapper CartItemEntity
Créez ensuite un wrapper similaire pour la classe CartItemEntity
.
Créez un fichier Swift nommé CartItem.swift
dans le répertoire 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)
}
}
Étant donné que la classe Core Data CartItem
d'origine comportait une propriété Fruittie
, nous avons également inclus une propriété Fruittie
dans le struct CartItem
. Bien que la classe CartItem
ne comporte aucune propriété facultative, la propriété count
a un type différent dans l'entité Room.
Mettre à jour les dépôts
Maintenant que les wrappers d'entité sont en place, vous devez mettre à jour DefaultCartRepository
et DefaultFruittieRepository
pour utiliser Room au lieu de Core Data.
Mettre à jour DefaultCartRepository
Commençons par la classe DefaultCartRepository
, qui est la plus simple des deux.
Ouvrez le fichier CartRepository.swift
dans le répertoire Sources/Repository
.
- Commencez par remplacer l'importation
CoreData
parsharedKit
:
import sharedKit
- Supprimez ensuite la propriété
NSManagedObjectContext
et remplacez-la par une propriétéCartDao
:
// Remove
private let managedObjectContext: NSManagedObjectContext
// Replace with
private let cartDao: any CartDao
- Mettez à jour le constructeur
init
pour initialiser la nouvelle propriétécartDao
:
init(cartDao: any CartDao) {
self.cartDao = cartDao
}
- Mettez ensuite à jour la méthode
addToCart
. Cette méthode exigeait d'extraire les articles de panier existants de Core Data, mais notre implémentation Room ne nécessite pas cette opération. Au lieu de cela, un nouvel article est inséré ou le nombre d'articles d'un panier existant est incrémenté.
func addToCart(fruittie: Fruittie) async throws {
try await cartDao.insertOrIncreaseCount(fruittie: fruittie.entity)
}
- Enfin, mettez à jour la méthode
getCartItems()
. Cette méthode appelle la méthodegetAll()
surCartDao
et mappe les entitésCartItemWithFruittie
à nos wrappersCartItem
.
func getCartItems() -> AsyncStream<[CartItem]> {
return cartDao.getAll().map { entities in
entities.map(CartItem.init(entity:))
}.eraseToStream()
}
Mettre à jour DefaultFruittieRepository
Pour migrer la classe DefaultFruittieRepository
, nous appliquons des modifications similaires à celles que nous avons appliquées pour DefaultCartRepository
.
Mettez à jour le fichier FruittieRepository comme suit:
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()
}
}
Remplacer les wrappers de propriété @FetchRequest
Nous devons également remplacer les wrappers de propriété @FetchRequest
dans les affichages SwiftUI. Le wrapper de propriété @FetchRequest
permet d'extraire des données à partir de Core Data et d'observer les modifications. Nous ne pouvons donc pas l'utiliser avec des entités Room. Utilisons plutôt UIModel pour accéder aux données des dépôts.
- Ouvrez
CartView
dans le fichierSources/UI/CartView.swift
. - Remplacez l'implémentation par le code suivant:
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)")
}
}
}
}
}
}
Mettre à jour ContentView
Mettez à jour ContentView
dans le fichier Sources/View/ContentView.swift
pour transmettre FruittieUIModel
à CartView
.
CartView(uiModel: uiModel)
Mettre à jour DataController
La classe DataController
de l'application iOS est chargée de configurer la pile Core Data. Étant donné que nous abandonnons Core Data, nous devons mettre à jour DataController
pour initialiser la base de données Room à la place.
- Ouvrez le fichier
DataController.swift
dansSources/Database
. - Ajoutez l'importation
sharedKit
. - Supprimez l'importation
CoreData
. - Instanciez la base de données Room dans la classe
DataController
. - Enfin, supprimez l'appel de méthode
loadPersistentStores
de l'initialiseurDataController
.
La classe finale devrait se présenter comme suit :
import Combine
import sharedKit
class DataController: ObservableObject {
let database = getPersistentDatabase()
init() {}
}
Mettre à jour l'injection de dépendances
La classe AppContainer
de l'application iOS est chargée d'initialiser le graphique de dépendance. Étant donné que nous avons mis à jour les dépôts pour utiliser Room au lieu de Core Data, nous devons mettre à jour AppContainer
pour transmettre les DAO Room aux dépôts.
- Ouvrez
AppContainer.swift
dans le dossierSources/DI
. - Ajoutez l'importation
sharedKit
. - Supprimez la propriété
managedObjectContext
de la classeAppContainer
. - Modifiez les initialisations
DefaultFruittieRepository
etDefaultCartRepository
en transmettant des DAO Room à partir de l'instanceAppDatabase
fournie parDataController
.
Lorsque vous avez terminé, la classe se présente comme suit :
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()
)
}
}
Enfin, mettez à jour FruittiesApp
dans main.swift
pour supprimer managedObjectContext
:
struct FruittiesApp: App {
@StateObject
private var appContainer = AppContainer()
var body: some Scene {
WindowGroup {
ContentView(appContainer: appContainer)
}
}
}
Compiler et exécuter l'application iOS
Enfin, une fois que vous avez compilé et exécuté l'application en appuyant sur ⌘R, l'application devrait démarrer avec la base de données migrée de Core Data vers Room.
8. Félicitations
Félicitations ! Vous avez migré des applications Android et iOS autonomes vers une couche de données partagée à l'aide de Room KMP.
Pour référence, voici la comparaison des architectures d'applications pour voir ce qui a été réalisé :
Avant
Android | iOS |
Architecture après la migration
En savoir plus
- Découvrez quelles autres bibliothèques Jetpack sont compatibles avec KMP.
- Lisez la documentation de Room KMP.
- Lisez la documentation de SQLite KMP.
- Consultez la documentation officielle de Kotlin Multiplatform.