SOLID-Prinzipien in Swift: Theorie und Praxis für die iOS-Entwicklung

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 SOLID für Swift-Entwickler wichtig ist

Jeder iOS-Entwickler stößt früher oder später auf das Problem von "Spaghetti-Code": Controller schwellen auf Tausende von Zeilen an, die Änderung einer Entität zerstört drei andere, und das Testen wird zum Albtraum. Die SOLID-Prinzipien sind fünf grundlegende Regeln des objektorientierten Designs, die helfen, flexiblen, wartbaren und testbaren Code in Swift zu erstellen.

In diesem Artikel werden wir jedes SOLID-Prinzip anhand konkreter Beispiele aus der iOS-Entwicklung analysieren. Sie erfahren, wie Sie diese Prinzipien in realen Projekten anwenden und typische Fehler vermeiden. Das Material ist sowohl für Junior-Entwickler als auch für erfahrene Fachleute nützlich, die ihr Wissen systematisieren möchten.



1. Single Responsibility Principle (SRP) — Prinzip der einzigen Verantwortung

Kernaussage: Jede Klasse sollte nur einen Grund für eine Änderung haben. Mit anderen Worten, eine Klasse sollte nur für eine Aufgabe verantwortlich sein.



Problem: Der "göttliche" ViewController

Ein typischer Fehler ist es, die gesamte Logik in den UIViewController zu packen. Betrachten wir ein schlechtes Beispiel:

class UserProfileViewController: UIViewController {    @IBOutlet weak var nameLabel: UILabel!        func loadUser() {        // 1. Netzwerklogik        let url = URL(string: "https://api.example.com/user")!        URLSession.shared.dataTask(with: url) { data, _, _ in            // 2. JSON-Parsing            let user = try! JSONDecoder().decode(User.self, from: data!)            // 3. Speichern in Core Data            self.saveToCoreData(user)            // 4. UI-Update            DispatchQueue.main.async {                self.nameLabel.text = user.name            }        }.resume()    }        private func saveToCoreData(_ user: User) { /* ... */ }}


Lösung: Aufteilung der Verantwortung

Teilen wir den Code in separate Komponenten auf:

// 1. Service für die Netzwerkarbeitclass UserNetworkService {    func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {        let url = URL(string: "https://api.example.com/user")!        URLSession.shared.dataTask(with: url) { data, _, error in            if let data = data {                do {                    let user = try JSONDecoder().decode(User.self, from: data)                    completion(.success(user))                } catch {                    completion(.failure(error))                }            } else if let error = error {                completion(.failure(error))            }        }.resume()    }}

// 2. Datenverwaltungs-Managerclass UserStorageManager { func save(_ user: User) { // Logik zum Speichern in Core Data / Realm }}

// 3. ViewModel (oder Presenter)class UserViewModel { private let networkService = UserNetworkService() private let storageManager = UserStorageManager() var onUserUpdated: ((User) -> Void)? func loadUser() { networkService.fetchUser { [weak self] result in switch result { case .success(let user): self?.storageManager.save(user) self?.onUserUpdated?(user) case .failure(let error): print(error.localizedDescription) } } }}

// 4. ViewController verwaltet jetzt nur noch die UIclass UserProfileViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! private let viewModel = UserViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.onUserUpdated = { [weak self] user in DispatchQueue.main.async { self?.nameLabel.text = user.name } } viewModel.loadUser() }}

Ergebnis: Jede Klasse hat einen Grund für eine Änderung. Das Testen und Warten solchen Codes ist deutlich

Blogs

Buchempfehlungen