Почему Code Review в Swift — это не про придирки
Представьте ситуацию: вы открываете пул-реквест коллеги, видите 500 строк Swift-кода и понимаете, что через 20 минут начнётся дейли. Знакомо? Code review (или код-ревью) — это не соревнование «кто найдёт больше опечаток». Это ваш шанс предотвратить баги в продакшене, улучшить архитектуру и передать знания команде.
В этой статье я собрал чек-лист code review для Swift, который использую сам. Никакой воды — только конкретные проверки, примеры кода и объяснения, почему это важно. Даже если вы пишете на Python, но заглядываете в мир iOS-разработки — материал будет полезен.
1. Безопасность памяти и опциональные типы
Swift гордится своей безопасностью памяти, но это не значит, что можно расслабиться. Самая частая проблема — force unwrapping. Каждый восклицательный знак — это потенциальный краш.
Что проверять:
- Force unwrap (
!) — допустим только в тестах или когда вы на 100% уверены в значении. В production-коде — красный флаг. - IUO (Implicitly Unwrapped Optionals) — используйте их только для IBOutlet или свойств, которые инициализируются после
init, но до первого использования. - Retain cycles — проверяйте замыкания на наличие
[weak self]или[unowned self].
// Плохо: force unwrap без проверки
let url = URL(string: "https://pythonlib.ru")! // Краш, если строка невалидна
// Хорошо: guard let
guard let url = URL(string: "https://pythonlib.ru") else {
print("Невалидный URL")
return
}
// Проверка retain cycle в замыкании
class NetworkManager {
var completionHandler: (() -> Void)?
func fetchData() {
completionHandler = { [weak self] in
guard let self = self else { return }
self.updateUI() // self теперь слабая ссылка
}
}
func updateUI() { /* ... */ }
}
Совет для code review: если видите ! — спрашивайте «почему здесь не guard или if let?». Это спасёт от многих падений.
2. Обработка ошибок: не глотайте исключения
Swift даёт мощный механизм do-catch, но разработчики часто его игнорируют. Типичный антипаттерн — try! или пустой catch.
Что проверять:
try!— почти всегда зло. Только если вы пишете тест.- Пустой
catch {}— вы просто теряете информацию об ошибке. Минимум — залогируйте её. - Проброс ошибок наверх (
throws) — функция должна либо обработать ошибку, либо явно её пробросить.
// Плохо: глотаем ошибку
do {
let data = try fetchData()
} catch {
// Ничего не делаем — ошибка потеряна
}
// Хорошо: логируем и показываем пользователю
do {
let data = try fetchData()
updateUI(with: data)
} catch let error as NetworkError {
showAlert(message: error.localizedDescription)
} catch {
print("Неизвестная ошибка: \(error.localizedDescription)")
showGenericError()
}
В code review для Swift всегда проверяйте, что Error не просто игнорируется. Пользователь должен понимать, что пошло не так.
3. Архитектура: MVC, MVVM или VIPER?
Неважно, какой паттерн вы выбрали — важно, чтобы код ему следовал. На code review вы видите, не превратился ли ViewController в «God Object» (класс, который делает всё).
Что проверять:
- Размер файла — если в одном файле больше 300-400 строк, это повод задуматься о разделении.
- Ответственность классов — ViewController не должен ходить в сеть или парсить JSON. Для этого есть сервисы и менеджеры.
- Зависимости — используете ли вы Dependency Injection? Если класс создаёт свои зависимости внутри (
let api = APIManager()), его сложно тестировать.
// Плохо: ViewController делает всё
class UserViewController: UIViewController {
func loadUser() {
let url = URL(string: "https://api.example.com/user")!
let data = try! Data(contentsOf: url) // Сетевой запрос прямо здесь
let json = try! JSONSerialization.jsonObject(with: data)
// ... и так далее
}
}
// Хорошо: разделение ответственности
class UserViewController: UIViewController {
private let viewModel: UserViewModel
init(viewModel: UserViewModel) { // Dependency Injection
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel.loadUser { [weak self] user in
self?.updateUI(with: user)
}
}
}
Вопрос на code review: «Можно ли этот класс переиспользовать в другом проекте?» Если нет — скорее всего, он слишком завязан на конкретную реализацию.
4. Производительность: CollectionView и работа с памятью
Медленный UI — одна из главных причин негативных отзывов. На code review обращайте внимание на работу с коллекциями и изображениями.
Что проверять:
- Переиспользование ячеек —
dequeueReusableCellобязателен. Если кто-то создаёт новую ячейку вcellForItemAt— это баг производительности. - Работа с изображениями — используйте
NSCacheили библиотеки типа Kingfisher. ИзбегайтеUIImage(data:)без кеширования. - Тяжёлые операции в main thread — парсинг JSON, работа с базой данных должны быть в фоне.
// Плохо: синхронная загрузка изображения
let image = UIImage(data: try! Data(contentsOf: imageURL)) // Блокирует UI
// Хорошо: асинхронная загрузка с кешем
DispatchQueue.global().async { [weak self] in
guard let data = try? Data(contentsOf: imageURL),
let image = UIImage(data: data) else { return }
DispatchQueue.main.async {
self?.imageView.image = image
}
}
На code review спрашивайте: «Где здесь может быть блокировка UI?». Если ответа нет — просите переписать.
5. Именование и код-стайл
Споры о табуляции и пробелах — это прошлый век. В Swift есть чёткие гайдлайны от Apple (Swift API Design Guidelines). На code review следите за ними.
Что проверять:
- Имена переменных и функций — читаемость важнее краткости.
fetchUserData()лучше, чем