Why Code Review in Swift Is Not About Nitpicking
\nImagine this: you open a colleague's pull request, see 500 lines of Swift code, and realize the daily standup starts in 20 minutes. Sound familiar? Code review is not a competition of "who can find more typos." It's your chance to prevent bugs in production, improve architecture, and share knowledge with the team.
\nIn this article, I've compiled a code review checklist for Swift that I use myself. No fluff — only specific checks, code examples, and explanations of why they matter. Even if you write in Python but are looking into the world of iOS development, this material will be useful.
\n\n1. Memory Safety and Optional Types
\nSwift prides itself on memory safety, but that doesn't mean you can let your guard down. The most common issue is force unwrapping. Every exclamation mark is a potential crash.
\n\nWhat to Check:
\n- \n
- Force unwrap (
!) — only acceptable in tests or when you are 100% sure of the value. In production code, it's a red flag. \n - IUO (Implicitly Unwrapped Optionals) — use them only for IBOutlets or properties that are initialized after
initbut before first use. \n - Retain cycles — check closures for
[weak self]or[unowned self]. \n
// Bad: force unwrap without checking\nlet url = URL(string: "https://pythonlib.ru")! // Crash if the string is invalid\n\n// Good: guard let\nguard let url = URL(string: "https://pythonlib.ru") else {\n print("Invalid URL")\n return\n}\n\n// Checking for retain cycle in a closure\nclass NetworkManager {\n var completionHandler: (() -> Void)?\n \n func fetchData() {\n completionHandler = { [weak self] in\n guard let self = self else { return }\n self.updateUI() // self is now a weak reference\n }\n }\n \n func updateUI() { /* ... */ }\n}\n\n\nCode review tip: if you see !, ask "why isn't this guard or if let?" This will save you from many crashes.
2. Error Handling: Don't Swallow Exceptions
\nSwift provides a powerful do-catch mechanism, but developers often ignore it. A typical anti-pattern is try! or an empty catch.
What to Check:
\n- \n
try!— almost always evil. Only use it if you're writing a test. \n - Empty
catch {}— you're just losing error information. At a minimum, log it. \n - Propagating errors upward (
throws) — a function should either handle the error or explicitly throw it. \n
// Bad: swallowing the error\ndo {\n let data = try fetchData()\n} catch {\n // Doing nothing — the error is lost\n}\n\n// Good: logging and showing to the user\ndo {\n let data = try fetchData()\n updateUI(with: data)\n} catch let error as NetworkError {\n showAlert(message: error.localizedDescription)\n} catch {\n print("Unknown error: \\(error.localizedDescription)")\n showGenericError()\n}\n\n\nIn a Swift code review, always check that Error is not simply ignored. The user should understand what went wrong.
3. Architecture: MVC, MVVM, or VIPER?
\nIt doesn't matter which pattern you chose — what matters is that the code follows it. During a code review, you can see if the ViewController has turned into a "God Object" (a class that does everything).
\n\nWhat to Check:
\n- \n
- File size — if one file has more than 300-400 lines, it's a reason to consider splitting it up. \n
- Class responsibility — a ViewController should not make network calls or parse JSON. That's what services and managers are for. \n
Blogs
Book Recommendations
Read also