Introduction : pourquoi le code legacy en Swift nécessite un refactoring
Travailler avec du code legacy est une partie inévitable de la vie d'un développeur iOS/macOS. Même si un projet a été initialement écrit en Swift, il accumule avec le temps de la dette technique : patterns obsolètes, absence de tests, architecture monolithique et mélange de Swift avec Objective-C. Le refactoring de ce code améliore non seulement la lisibilité, mais réduit également le coût de maintenance, accélère l'ajout de nouvelles fonctionnalités et diminue le nombre de bugs.
Dans cet article, nous allons analyser des techniques concrètes de refactoring de code Swift legacy : de la suppression du force unwrapping à la migration de MVC vers MVVM. Tous les exemples peuvent être appliqués aussi bien aux projets iOS qu'aux projets macOS.
1. Évaluation et préparation : par où commencer le refactoring
Avant de modifier le code, il est important de comprendre son état actuel. Utilisez les outils intégrés de Xcode et les métriques.
1.1. Analyse des avertissements et des erreurs
Ouvrez le projet dans Xcode et allez dans Report Navigator (Cmd+9). Faites attention à :
- Yellow warnings — API obsolètes (deprecated), variables inutilisées.
- Red errors — erreurs syntaxiques qui peuvent être la conséquence d'une migration Swift incomplète.
- SwiftLint/SwiftFormat — connectez un linter pour détecter automatiquement les violations du code style.
1.2. Recherche de "drapeaux rouges" dans le code
Parcourez le projet à l'aide de la recherche (Cmd+Shift+F) pour les patterns suivants :
!— force unwrapping (valeurs optionnelles qui plantent l'application).as!— force casting (cast de types sans vérification).try!— ignorance des erreurs.NSObject— héritage de NSObject sans nécessité (Swift pur ne l'exige pas).#selector— sélecteurs sous forme de chaînes, qu'il est préférable de remplacer par#selectoravec type.
// Mauvais (legacy)let name = user.name! // force unwrap — crash si nil
// Bon (refactoring)guard let name = user.name else { return}1.3. Écrire des tests avant les modifications
Le refactoring sans tests est un risque. S'il n'y a pas de tests, commencez par en ajouter pour les modules critiques. Utilisez XCTest pour les tests unitaires et XCUITest pour l'interface utilisateur.
Exemple d'un test simple pour une fonction legacy :
class LegacyCalculatorTests: XCTestCase { func testCalculateTotal() { let calculator = LegacyCalculator() let result = calculator.calculateTotal(items: [10, 20, 30]) XCTAssertEqual(result, 60) }}2. Techniques principales de refactoring du code Swift
Passons aux étapes pratiques que vous pouvez appliquer dès maintenant.
2.1. Remplacement du force unwrapping par des patterns sécurisés
Le problème le plus fréquent dans le code legacy est l'utilisation généralisée de !. Remplacez-les par guard let, if let ou ?? (nil-coalescing).
// Avant le refactoringfunc getUserName() -> String { return user.name!}
// Après le refactoringfunc getUserName() -> String { return user.name ?? "Unknown"}2.2. Se débarrasser du Massive View Controller (MVC → MVVM)
Les projets legacy souffrent souvent de contrôleurs "lourds". Extrayez la logique métier dans des services séparés ou des ViewModel.
// Legacy ViewController (fragment)class ProfileViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // Logique métier directement dans le contrôleur let user = DataManager.shared.fetchUser() nameLabel.text = user.name?.capitalized }}
// Après le refactoringclass ProfileViewModel { private let userService: UserService var userName: String { return userService.currentUs