Principes SOLID en Swift : théorie et pratique pour le développement iOS

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 SOLID est important pour un développeur Swift

Chaque développeur iOS rencontre tôt ou tard le problème du "code spaghetti" : les contrôleurs gonflent jusqu'à des milliers de lignes, la modification d'une entité en casse trois autres, et les tests deviennent un cauchemar. Les principes SOLID sont cinq règles fondamentales de conception orientée objet qui aident à créer un code flexible, maintenable et testable en Swift.

Dans cet article, nous analyserons chaque principe SOLID avec des exemples concrets issus du développement iOS. Vous apprendrez à appliquer ces principes dans des projets réels tout en évitant les erreurs typiques. Ce contenu sera utile aussi bien aux développeurs Juniors qu'aux spécialistes expérimentés souhaitant systématiser leurs connaissances.



1. Single Responsibility Principle (SRP) — Principe de responsabilité unique

Essence : Chaque classe ne doit avoir qu'une seule raison de changer. Autrement dit, une classe ne doit être responsable que d'une seule tâche.



Problème : Le ViewController "divin"

Une erreur typique consiste à placer toute la logique dans UIViewController. Examinons un mauvais exemple :

class UserProfileViewController: UIViewController {    @IBOutlet weak var nameLabel: UILabel!        func loadUser() {        // 1. Logique réseau        let url = URL(string: "https://api.example.com/user")!        URLSession.shared.dataTask(with: url) { data, _, _ in            // 2. Parsing JSON            let user = try! JSONDecoder().decode(User.self, from: data!)            // 3. Sauvegarde dans Core Data            self.saveToCoreData(user)            // 4. Mise à jour de l'UI            DispatchQueue.main.async {                self.nameLabel.text = user.name            }        }.resume()    }        private func saveToCoreData(_ user: User) { /* ... */ }}


Solution : Séparation des responsabilités

Séparons le code en composants distincts :

// 1. Service pour le travail réseauclass 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. Gestionnaire de stockage de donnéesclass UserStorageManager { func save(_ user: User) { // Logique de sauvegarde dans Core Data / Realm }}

// 3. ViewModel (ou 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 ne gère désormais que l'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() }}

Résultat : Chaque classe a une seule raison de changer. Tester et maintenir un tel code est considérablement

Blogs

Book Recommendations