Code Review in Swift: 10 Things I Check First

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Code Review in Swift Is Not About Nitpicking

\n

Imagine 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.

\n

In 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\n

1. Memory Safety and Optional Types

\n

Swift 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\n

What 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 init but before first use.
  • \n
  • Retain cycles — check closures for [weak self] or [unowned self].
  • \n
\n\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\n

Code review tip: if you see !, ask "why isn't this guard or if let?" This will save you from many crashes.

\n\n

2. Error Handling: Don't Swallow Exceptions

\n

Swift provides a powerful do-catch mechanism, but developers often ignore it. A typical anti-pattern is try! or an empty catch.

\n\n

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
\n\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\n

In a Swift code review, always check that Error is not simply ignored. The user should understand what went wrong.

\n\n

3. Architecture: MVC, MVVM, or VIPER?

\n

It 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\n

What to Check:

\n