How to Write Clean Code in Swift: A Guide for Those Who Respect Themselves and Their Colleagues
\n\nEvery developer has at least once opened a project written six months ago and couldn't understand what was going on. Or received a comment on a code review: "This could have been written simpler." Sound familiar? If yes — this article is for you.
\n\nClean code in Swift is not just a perfectionist's whim. It is a tool that saves hours of debugging and nerves. When you write neatly, you not only speed up your own work but also make the project transparent for the team. Let's break down how to turn your Swift code into a work of art.
\n\nRule #1: Naming is Everything
\n\nThe most common sin is to name a variable x or temp. Then another developer comes along and wonders: "What is x? A coordinate? A quantity?"
In Swift, it is customary to give names that read like sentences. This is not just beautiful — it helps to immediately understand what the code does.
\n\n// Bad\nlet a = 5.0\nvar b = "John"\n\n// Good\nlet discountRate = 5.0\nvar userName = "John"\n\nThe same rule applies to functions and methods. The name should answer the question "What does this method do?" Use verbs in the imperative mood:
\n\n// Bad\nfunc data() -> [String] { ... }\n\n// Good\nfunc fetchUsers() -> [String] { ... }\n\nAnd if a function returns a boolean value, start the name with is, has, or contains:
func isUserLoggedIn() -> Bool\nfunc hasValidEmail() -> Bool\n\nRule #2: Optionals Are Not Enemies, But Tools
\n\nMany beginners are afraid of optionals and try to get rid of them at any cost. They write if let everywhere or, even worse, force unwrap. But clean code in Swift is code that safely works with nil.
Remember: ! is a cry for help. Use it only if you are absolutely sure the value is not nil (for example, when working with IBOutlet).
// Dangerous code\nlet name = optionalName!\n\n// Safe option\nguard let name = optionalName else {\n return\n}\nprint(name)\n\nAnd for optional chaining, use ? and nil-coalescing:
let userCity = user?.address?.city ?? "Unknown"\n\nThis code won't crash even if user or address turn out to be nil. And it reads like a normal sentence: "The user's city or 'Unknown'".
Rule #3: Structs Instead of Classes (Where Possible)
\n\nSwift has both classes and structs. But many people habitually use classes everywhere. And that's a mistake. Structs are value types, and they are safer in multithreaded code. Plus, they are copied, not referenced, which reduces the risk of side effects.
\n\nUse a struct if:
\n- \n
- You need to store data without complex logic \n
- There is no need for inheritance \n
- The object should not be mutable after creation \n
// Better as a struct\nstruct User {\n let id: Int\n var name: String\n}\n\n// Class — only if inheritance or reference semantics are needed\nclass ViewController: UIViewController { ... }\n\nBy the way, Apple recommends using structs for data models. This is one of the main principles of clean code in Swift.
\n\nRule #4: Protocols Are Your Best Friends
\n\nProtocols in Swift are not just interfaces. They are a way to make code flexible and testable. Instead of tying yourself to a specific class, work through a protocol.
\n\nprotocol NetworkServiceProtocol {\n func fetchData(completion: @escaping (Result<Data, Error>) -> Void)\n}\n\nclass APIService: NetworkServiceProtocol { ... }\nclass MockService: NetworkServiceProtocol { ... }\n\nNow you can easily replace the real service with a mock for tests. And you don't need to change the code in other