Code review в Swift: что проверять и как давать обратную связь
Code review — это не просто формальная процедура, а мощный инструмент повышения качества кода и профессионального роста команды. Для iOS/macOS разработчиков на Swift грамотно проведённое ревью помогает избежать багов, улучшить архитектуру и поддерживать единый стиль кода. В этой статье мы разберём, на что обращать внимание при code review Swift-проектов, и как давать обратную связь, чтобы она приносила пользу, а не демотивировала.
1. Архитектура и разделение ответственности
Первое, что стоит оценить при ревью — насколько хорошо код соответствует выбранной архитектуре (MVC, MVVM, VIPER, SwiftUI + Combine). Плохая архитектура ведёт к «God-объектам» и сложности поддержки.
Что проверять:
- Не смешивает ли ViewController бизнес-логику и UI-код?
- Вынесена ли сетевая логика в отдельные сервисы (NetworkService)?
- Используются ли протоколы для абстракции зависимостей?
- Не слишком ли много ответственности у одного класса?
// ❌ Плохо: ViewController знает про URLSession и парсинг JSONclass UserViewController: UIViewController { func fetchUser() { URLSession.shared.dataTask(with: URL(string: "https://api.example.com/user")!) { data, _, _ in let user = try? JSONDecoder().decode(User.self, from: data!) DispatchQueue.main.async { self.updateUI(with: user) } }.resume() }}
// ✅ Хорошо: логика вынесена в сервисprotocol UserServiceProtocol { func fetchUser(completion: @escaping (Result<User, Error>) -> Void)}
class UserViewModel { private let userService: UserServiceProtocol init(userService: UserServiceProtocol) { self.userService = userService } func loadUser() { userService.fetchUser { [weak self] result in // обработка результата } }}Совет по обратной связи: Вместо «Этот класс слишком большой» напишите: «Я вижу, что UserViewController отвечает и за сетевые запросы, и за UI. Может, вынести запрос в отдельный сервис, как мы обсуждали на ретро? Это упростит тестирование».
2. Безопасность памяти и управление жизненным циклом
Swift — язык с автоматическим подсчётом ссылок (ARC), но это не освобождает разработчика от заботы о циклах сильных ссылок. Ошибки в этой области — частая причина утечек памяти в iOS-приложениях.
Что проверять:
- Используется ли
[weak self]или[unowned self]в замыканиях, особенно при работе с async/await и Combine? - Не создаётся ли циклическая ссылка между делегатом и владельцем?
- Правильно ли освобождаются ресурсы (наблюдатели, таймеры)?
// ❌ Потенциальная утечка: сильная ссылка на self в замыканииclass LocationManager { var onLocationUpdate: ((CLLocation) -> Void)? func startUpdating() { someAsyncAPI { location in self.onLocationUpdate?(location) // self захвачен сильно } }}
// ✅ Исправлено с [weak self]class LocationManager { var onLocationUpdate: ((CLLocation) -> Void)? func startUpdating() { someAsyncAPI { [weak self] location in guard let self = self else { return } self.onLocationUpdate?(location) } }}Совет по обратной связи: Указывайте не только на проблему, но и на последствия: «Здесь может возникнуть утечка памяти, если LocationManager будет жить дольше, чем нужно. Давай добавим [weak self], чтобы разорвать цикл».
3. Обработка ошибок и опциональность
Swift — строго типизированный язык с мощной системой Optional. Плохая обработка опциональных значений — одна из гл