Clean Code in Swift: Principles and Examples for Beginners
Every iOS or macOS developer sooner or later faces a problem: code that worked perfectly yesterday becomes unreadable today. This is not a flaw of the Swift language — it is the result of neglecting the principles of Clean Code. Clean code is not just about aesthetics. It is the foundation of project maintainability, reduced debugging time, and comfortable teamwork. In this article, we will break down the key principles of Clean Code with Swift examples that will help you write code understandable not only to the computer but also to people.
1. Naming: Variables, Constants, and Functions
The first and most important principle — the name should answer the question "Why?", not "How?". In Swift, thanks to strong typing, you can omit the type in the name, but not the intent.
Bad example:
var d: Double = 3.14var arr = [String]()func calc(a: Double, b: Double) -> Double { return a * b}Good example (Clean Code):
let piValue: Double = 3.14159var userNames: [String] = []func calculateRectangleArea(width: Double, height: Double) -> Double { return width * height}Naming rules in Swift:
- Use camelCase for variables and functions.
- Avoid abbreviations (
btn→submitButton). - For boolean variables, use prefixes
is,has,should(e.g.,isUserLoggedIn). - Function names should start with a verb:
fetchData(),saveToCache().
2. Functions: One Task — One Function
The Single Responsibility principle applies not only to classes but also to functions. If a function does more than one thing — it needs to be split.
Bad example: function does everything at once
func processUserData(user: User) { // Validation guard user.email.contains("@") else { return } // Save to database Database.shared.save(user) // Send notification NotificationCenter.default.post(name: .userDidRegister, object: nil) // Update UI DispatchQueue.main.async { self.label.text = "Welcome, \\(user.name)" }}Good example: split into small functions
func registerUser(_ user: User) { guard validateUser(user) else { return } saveUserToDatabase(user) postRegistrationNotification() updateUI(for: user)}
private func validateUser(_ user: User) -> Bool { return user.email.contains("@") && !user.name.isEmpty}
private func saveUserToDatabase(_ user: User) { Database.shared.save(user)}
private func postRegistrationNotification() { NotificationCenter.default.post(name: .userDidRegister, object: nil)}
private func updateUI(for user: User) { DispatchQueue.main.async { self.welcomeLabel.text = "Welcome, \\(user.name)" }}Tip: If you can't come up with a short name for a function, it's likely doing too much.
3. Comments: Less is Better, but Accurate
In Clean Code, comments are a sign of bad code. Good code should document itself. However, there are exceptions: complex business logic, working with legacy code, or public APIs.
Redundant comment (bad):
// Increment counter by 1counter += 1Useful comment (good):
// Use Decimal to avoid rounding errors when working with currencylet price = Decimal(string: "19.99") ?? .zeroRules for working with comments:
- Don't write the obvious — the code reads itself.
- Use
// MARK: -for