Clean Code en Swift : principes et exemples pratiques
\n\nLe code propre (Clean Code) n'est pas simplement un terme à la mode, mais une compétence fondamentale pour tout développeur professionnel. Dans l'écosystème Swift (iOS/macOS), la qualité du code influence directement la vitesse de développement, le nombre de bogues et la facilité de maintenance du projet. Dans cet article, nous analyserons les principes clés du Clean Code appliqués à Swift et montrerons leur mise en œuvre à travers des exemples pratiques.
\n\n1. Nommage : parlez le langage du domaine métier
\n\nUn bon nom de variable, de fonction ou de classe remplace les commentaires. En Swift, avec son système de types riche et ses valeurs optionnelles, un nommage correct est particulièrement important.
\n\nMauvais exemple :
\nfunc calc(a: [String: Any]) -> Double {\n var x = 0.0\n for (_, v) in a {\n if let n = v as? Double {\n x += n\n }\n }\n return x\n}\n\nBon exemple (Clean Code) :
\nfunc calculateTotalPrice(of products: [Product]) -> Double {\n return products.reduce(0) { $0 + $1.price }\n}\n\nPrincipes de nommage en Swift :
\n- \n
- La clarté prime sur la concision :
remove(at:)est meilleur querem(at:). \n - Utilisez des noms pour les types :
UserViewController,NetworkManager. \n - Des verbes pour les fonctions :
fetchData(),saveToCache(). \n - Variables booléennes : commencez par
is,has,should—isEnabled,hasContent. \n - Évitez les abréviations :
backgroundColorau lieu debgColor. \n
2. Principe de responsabilité unique (SRP) en Swift
\n\nChaque classe, structure ou fonction doit avoir une seule raison de changer. Dans le développement iOS, cela est particulièrement pertinent pour les ViewController, qui se transforment souvent en "objets divins".
\n\nViolation du SRP (ViewController typique) :
\nclass ProfileViewController: UIViewController {\n @IBOutlet weak var nameLabel: UILabel!\n @IBOutlet weak var avatarImageView: UIImageView!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n // Chargement des données\n let url = URL(string: "https://api.example.com/user")!\n let data = try! Data(contentsOf: url)\n let json = try! JSONSerialization.jsonObject(with: data) as! [String: Any]\n nameLabel.text = json["name"] as? String\n \n // Chargement de l'avatar\n let imageUrl = URL(string: json["avatar"] as! String)!\n let imageData = try! Data(contentsOf: imageUrl)\n avatarImageView.image = UIImage(data: imageData)\n \n // Sauvegarde dans le cache\n UserDefaults.standard.set(json, forKey: "cachedUser")\n }\n}\n\nRefactoring avec respect du SRP :
\n// 1. Service séparé pour le réseau\nclass UserNetworkService {\n func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {\n // ... logique réseau\n }\n}\n\n// 2. Gestionnaire de cache séparé\nclass UserCacheManager {\n func saveUser(_ user: User) {\n // ... logique de mise en cache\n }\n}\n\n// 3. ViewModel pour la logique métier\nclass ProfileViewModel {\n private let networkService = UserNetworkService()\n private let cacheManager = UserCacheManager()\n \n func loadProfile(completion: @escaping (User) -> Void) {\n networkService.fetchUser { [weak self] result in\n switch result {\n case .success(let user):\n self?.cacheManager.saveUser(user)\n completion(user)\n case .failure:\n // Chargement depuis le cache en cas d'erreur\n if let cachedUser = self?.cacheManager.lo