Réusinage du code legacy en Swift : guide étape par étape

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi refactoriser le code legacy en Swift ?



Travailler avec du code legacy est une partie inévitable de la vie d'un développeur iOS/macOS. Les projets écrits dans les premières versions de Swift (2.x, 3.x) ou même en Objective-C contiennent souvent des classes monolithiques, des duplications de logique et un manque de modularité. Sans refactorisation, ce code devient une source de bugs et ralentit l'ajout de nouvelles fonctionnalités.



Dans cet article, nous allons analyser des stratégies pratiques de refactorisation de code legacy en Swift. Vous apprendrez comment améliorer l'architecture en toute sécurité sans casser les fonctionnalités existantes. Tous les exemples sont applicables aussi bien aux applications iOS que macOS.



1. Analyse et préparation : par où commencer ?



Avant de modifier le code, il est nécessaire de comprendre son état actuel. Sans analyse, vous risquez de casser des fonctionnalités qui fonctionnent.



1.1. Couverture par les tests (Characterization Tests)



La première étape est d'écrire des tests de caractérisation. Ils figent le comportement actuel du code, même s'il semble incorrect. Ce n'est qu'après cela que vous pouvez apporter des modifications en toute sécurité.



// Exemple de fonction legacy : UserManager.swiftclass UserManager {    func fetchUser(by id: Int) -> String {        // Implémentation obsolète avec force unwrap        let dict = ["id": id, "name": "John"] as [String : Any]        return dict["name"] as! String    }}

// Test de caractérisationimport XCTestclass UserManagerTests: XCTestCase { func testFetchUserReturnsName() { let manager = UserManager() let result = manager.fetchUser(by: 1) XCTAssertEqual(result, "John") }}


1.2. Inventaire des "mauvaises pratiques"



Dressez une liste des patterns problématiques dans le code :

  • Force unwrapping (!) — la principale source de crashs.
  • Singletons globaux — rendent les tests difficiles.
  • Nombres et chaînes magiques — réduisent la lisibilité.
  • Méthodes longues (plus de 50 lignes) — violent le SRP.
  • Absence de types (utilisation de Any).


2. Refactorisation pas à pas : du simple au complexe



Il est préférable de refactoriser le code legacy par petites étapes. Chaque modification doit être réversible et accompagnée de tests.



2.1. Se débarrasser du force unwrapping



Remplacez ! par des constructions sûres : guard let ou if let.



// Avant la refactorisationclass NetworkService {    func loadData() -> Data {        let url = URL(string: "https://api.example.com")!        return try! Data(contentsOf: url)    }}

// Après la refactorisationclass NetworkService { enum NetworkError: Error { case invalidURL case dataLoadingFailed } func loadData() throws -> Data { guard let url = URL(string: "https://api.example.com") else { throw NetworkError.invalidURL } do { return try Data(contentsOf: url) } catch { throw NetworkError.dataLoadingFailed } }}


2.2. Décomposition des classes "divines"



Les classes legacy violent souvent le principe de responsabilité unique (SRP). Divisez-les en entités plus petites.



// Problème : la classe fait toutclass ProfileViewController: UIViewController {    func fetchProfile() { /* requête réseau */ }    func parseJSON() { /* parsing */ }    func updateUI() { /* mise à jour de l'UI */ }    func saveToCoreData() { /* sauvegarde */ }}

// Solution : séparation en modulesprotocol ProfileRepository { func fetchProfile(completion: @escaping (Result<Profile, Error>) -> Void)}

class ProfileNetworkRepository: ProfileRepository { func fetchProfile(completion: @escaping (Result<Profile, Error>) -> Void) { // Logique réseau

Blogs

Book Recommendations