Avec vos tests unitaires en place, vous êtes maintenant prêt à améliorer et à étendre les fonctionnalités de votre ViewModel. En effet, notre ViewModel, enfin son interface publique (ou API) fait à présent office de contrat définissant clairement quels inputs et outputs peuvent être utilisés par d’autres parties de l’application. Mais il ne fonctionne pas encore. D’ailleurs, nos tests unitaires le confirment. C’est pour cela que nous devons migrer la logique métier.
Récupérez le code à refactoriser
Voici une vidéo qui montre les principales étapes pour identifier le code à refactoriser.
Comme vous le savez maintenant, pour transformer efficacement notre application en suivant l'architecture MVVM, il est crucial de séparer les préoccupations ou responsabilités en identifiant les parties de la logique métier qui doivent être transférées du fichier View (“HomeScreenView”) vers le ViewModel (“HomeScreenViewModel”).
Souvenez-vous, dans la section “Identifiez les correspondances dans la View”, nous avons listé les éléments qui doivent être migrés de la View vers le ViewModel
Pour rappel, ces éléments sont :
Inputs :
Recherche de projets : Texte saisi dans la barre de recherche.
Affichage du formulaire d'ajout de projet : Clic sur le bouton Plus.
Ajout d’un projet
Suppression de projet : Clic sur l'icôneTrash.
Outputs :
Texte statique : "Accueil" et "EPCollaboratif".
Liste des projets filtrés : Mise à jour de la View en fonction de la recherche et de l'ajout ou suppression de projets.
Éléments dynamiques : La propriété
projectStore
doit être déplacée dans le ViewModel et être améliorée par la suite pour représenter les projets à injecter lors de l’initialisation de l’écran “Home”.
Intégrez et ajustez le code dans le ViewModel
Pour transférer efficacement le code depuis la View vers le ViewModel, il est essentiel de suivre une méthode structurée. En effet, la migration de la logique métier ne se résume pas à un simple copier/coller des éléments identifiés précédemment. Il faudra bien souvent ajuster la logique afin de rendre notre ViewModel fonctionnel, sans changer ce que la View envoie ni ce que le ViewModel renvoie. Procédons alors par étape, une fonctionnalité à la fois et en exécutant les tests unitaires associés après chaque migration. Cela permet, petit à petit, de vérifier le bon fonctionnement du ViewModel.
Voici une vidéo qui montre les principales étapes pour intégrer et ajuster le code dans le ViewModel.
Commençons par migrer le contenu la propriétéfilteredProject
qui contient la logique de filtrage des projets à afficher en fonction du contenu de la propriétésearchText
. Nous allons ensuite remplacer l’utilisation duprojectStore
par la propriétéprojects
que nous avions déclarée comme output.
import SwiftUI
class HomeScreenViewModel: ObservableObject {
// MARK: - Outputs
@Published var searchText: String = ""
@Published var showAddProjectView: Bool = false
@Published var projects: [Project] = []
var filteredProjects: [Project] {
if searchText.isEmpty {
return projects.projects
} else {
return projects.projects.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
}
let titleText = "EPCollaboratif"
let homeText = "Accueil"
// MARK: - Init
init() {
}
// MARK: - Inputs
func add(_ project: Project) {
}
func deleteProject(at index: Int) {
}
}
Mais du coup pourquoi garder la propriétéprojects
comme output si on expose aussifilteredProject
?
Bonne question ! Il n’y a plus besoin de rendre la propriétéprojects
publique carfilteredProject
fournit déjà les informations nécessaires. Cependant, nous avons encore besoin de pouvoir injecter une liste de projets dans notre ViewModel. Nous allons donc rendre cette propriété privée et l’initialiser dans le constructeur du ViewModel via la fonctioninit()
.
Voici comment implémenter cela dans votre code.
import SwiftUI
class HomeScreenViewModel: ObservableObject {
// MARK: - Private
@Published private var projects: [Project] = []
// MARK: - Outputs
@Published var searchText: String = ""
@Published var showAddProjectView: Bool = false
var filteredProjects: [Project] {
if searchText.isEmpty {
return projects.projects
} else {
return projects.projects.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
}
let titleText = "EPCollaboratif"
let homeText = "Accueil"
// MARK: - Init
init(projects: [Project]) {
self.projects = projects
}
// MARK: - Inputs
func add(_ project: Project) {
}
func deleteProject(at index: Int) {
}
}
Il ne nous reste plus qu'à migrer le contenu de la propriété qui contient la logique d’ajout et de suppression d’un projet dans la liste de projets. Les deux fonctions add
etdeleteProject
sont simples à migrer.
Pour ajouter un projet, on l’insère dans la liste de projets du ViewModel. Mais attention, il faut bien vérifier avant que le projet n’existe pas déjà dans la liste. Voici le code associé.
func add(_ project: Project) {
guard !projects.contains(project) else { return }
projects.append(project)
}
Pour la fonction de suppression d’un projet, c’est tout aussi simple. Avant de passer à l’action, assurez-vous que l’index du projet souhaité peut être trouvé dans la liste de projets. En effet, tenter d’accéder à un index qui n’existe pas dans une liste fait automatiquement crasher l’application ! Voici le code associé.
func deleteProject(at index: Int) {
guard projects.indices.contains(index) else { return }
projects.removeAll { $0.id == filteredProjects[index].id }
}
Pour voir le résultat final de cette migration, jetez un œil au fichier "HomeScreenViewModel".
Testez et validez le ViewModel
Après avoir intégré et ajusté le code dans le ViewModel, il est essentiel de valider cette intégration en utilisant des tests unitaires. Cela garantit que chaque modification fonctionne comme prévu et que l'application reste stable.
Pour cela, allez dans le menu Produit puis Test ou utilisez le raccourci Cmd+U pour exécuter tous les tests. Assurez-vous que tous les tests sont au vert. Vous devriez avoir une liste similaire à celle-ci.
Ici, vous pouvez constater la bonne exécution des tests, avec des résultats tous au vert. N’hésitez pas aussi à consulter les temps d'exécution et à vérifier si ceux-ci ne sont pas trop longs.
À vous de jouer
Contexte
Vous avez appris comment identifier et transférer la logique métier depuis la View vers le ViewModel. Il est maintenant temps de mettre cela en pratique en refactorisant le fichier "AddTaskView".
Consignes
Reprenez le code à refactoriser que vous avez identifié dans le chapitre “Analysez vos écrans”.
Transférez le code dans le ViewModel.
Testez et validez le ViewModel.
Livrable
Un fichier “AddTaskViewModel” mis à jour avec la logique métier transférée depuis la View.
Des tests unitaires validant les modifications apportées au ViewModel.
En résumé
La migration de la logique métier de la View vers le ViewModel est essentielle pour suivre l'architecture MVVM et séparer les responsabilités.
Il faut toujours ajuster le code du ViewModel et de la View pour qu'il fonctionne dans ce nouveau contexte.
Le ViewModel définit clairement les inputs et les outputs utilisables (publiques) ou non (privés) par d'autres parties de l'application.
Les propriétés privées dans le ViewModel sont initialisées et gérées pour optimiser l'accès et la manipulation des données, sans exposer directement les détails au reste de l'application.
Les tests unitaires doivent être mis à jour selon les modifications du ViewModel et doivent être exécutés régulièrement pour vérifier que chaque changement maintient l'intégrité fonctionnelle.
Vous savez comment transférer la logique métier de la View vers le ViewModel. Vous avez aussi toutes les clés pour valider les tests. La prochaine étape est de simplifier le fichier View et d’intégrer le ViewModel pour une application plus maintenable et performante.