Clean Code in Swift: Principles and Practical Examples
\n\nClean Code is not just a trendy term, but a fundamental skill for a professional developer. In the Swift ecosystem (iOS/macOS), code quality directly affects development speed, the number of bugs, and the ease of project maintenance. In this article, we will break down the key principles of Clean Code as applied to Swift and show their implementation with practical examples.
\n\n1. Naming: Speak the Language of the Domain
\n\nA good name for a variable, function, or class replaces comments. In Swift, with its rich type system and optional values, proper naming is especially important.
\n\nBad example:
\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\nGood example (Clean Code):
\nfunc calculateTotalPrice(of products: [Product]) -> Double {\n return products.reduce(0) { $0 + $1.price }\n}\n\nNaming principles in Swift:
\n- \n
- Clarity over brevity:
remove(at:)is better thanrem(at:). \n - Use nouns for types:
UserViewController,NetworkManager. \n - Verbs for functions:
fetchData(),saveToCache(). \n - Boolean variables: start with
is,has,should—isEnabled,hasContent. \n - Avoid abbreviations:
backgroundColorinstead ofbgColor. \n
2. Single Responsibility Principle (SRP) in Swift
\n\nEach class, structure, or function should have only one reason to change. In iOS development, this is especially relevant for ViewControllers, which often turn into "god objects".
\n\nSRP violation (typical ViewController):
\nclass ProfileViewController: UIViewController {\n @IBOutlet weak var nameLabel: UILabel!\n @IBOutlet weak var avatarImageView: UIImageView!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n // Loading data\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 // Loading 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 // Saving to cache\n UserDefaults.standard.set(json, forKey: "cachedUser")\n }\n}\n\nRefactoring with SRP compliance:
\n// 1. Separate service for network operations\nclass UserNetworkService {\n func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {\n // ... network logic\n }\n}\n\n// 2. Separate cache manager\nclass UserCacheManager {\n func saveUser(_ user: User) {\n // ... caching logic\n }\n}\n\n// 3. ViewModel for business logic\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 // Load from cache on error\n if let cachedUser = self?.cacheManager.lo