Refactoring von Legacy-Code in Swift: Schritt-für-Schritt-Anleitung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum Legacy-Code in Swift refaktorisieren?



Die Arbeit mit Legacy-Code ist ein unvermeidlicher Teil des Lebens eines iOS/macOS-Entwicklers. Projekte, die in frühen Versionen von Swift (2.x, 3.x) oder sogar Objective-C geschrieben wurden, enthalten oft monolithische Klassen, duplizierte Logik und mangelnde Modularität. Ohne Refaktorisierung wird solcher Code zu einer Fehlerquelle und verlangsamt die Implementierung neuer Funktionen.



In diesem Artikel werden wir praktische Strategien zur Refaktorisierung von Legacy-Code in Swift durchgehen. Sie lernen, wie Sie die Architektur sicher verbessern können, ohne bestehende Funktionalität zu beeinträchtigen. Alle Beispiele sind sowohl für iOS- als auch für macOS-Anwendungen anwendbar.



1. Analyse und Vorbereitung: Wo anfangen?



Bevor Sie den Code ändern, ist es notwendig, seinen aktuellen Zustand zu verstehen. Ohne Analyse riskieren Sie, funktionierende Funktionalität zu zerstören.



1.1. Testabdeckung (Charakterisierungstests)



Der erste Schritt ist das Schreiben von Charakterisierungstests. Sie erfassen das aktuelle Verhalten des Codes, selbst wenn es fehlerhaft erscheint. Erst danach können Sie sicher Änderungen vornehmen.



// Example of a legacy function: UserManager.swiftclass UserManager {    func fetchUser(by id: Int) -> String {        // Outdated implementation with force unwrap        let dict = ["id": id, "name": "John"] as [String : Any]        return dict["name"] as! String    }}

// Characterization testimport XCTestclass UserManagerTests: XCTestCase { func testFetchUserReturnsName() { let manager = UserManager() let result = manager.fetchUser(by: 1) XCTAssertEqual(result, "John") }}


1.2. Bestandsaufnahme der "schlechten Praktiken"



Erstellen Sie eine Liste problematischer Muster im Code:

  • Erzwungenes Entpacken (!) — die Hauptursache für Abstürze.
  • Globale Singletons — erschweren das Testen.
  • Magische Zahlen und Strings — verringern die Lesbarkeit.
  • Lange Methoden (mehr als 50 Zeilen) — verletzen das SRP.
  • Fehlende Typen (Verwendung von Any).


2. Schrittweise Refaktorisierung: Vom Einfachen zum Komplexen



Die Refaktorisierung von Legacy-Code erfolgt am besten in kleinen Schritten. Jede Änderung sollte reversibel und von Tests begleitet sein.



2.1. Beseitigung des erzwungenen Entpackens



Ersetzen Sie ! durch sichere Konstrukte: guard let oder if let.



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

// After refactoringclass 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. Zerlegung von "God"-Klassen



Legacy-Klassen verletzen oft das Prinzip der einzigen Verantwortung (SRP). Zerlegen Sie sie in kleinere Einheiten.



// Problem: the class does everythingclass ProfileViewController: UIViewController {    func fetchProfile() { /* network request */ }    func parseJSON() { /* parsing */ }    func updateUI() { /* UI update */ }    func saveToCoreData() { /* saving */ }}

// Solution: separation into modulesprotocol ProfileRepository { func fetchProfile(completion: @escaping (Result<Profile, Error>) -> Void)}

class ProfileNetworkRepository: ProfileRepository { func fetchProfile(completion: @escaping (Result<Profile, Error>) -> Void) { // Network logic

Blogs

Buchempfehlungen