На что вы обращаете внимание при код-ревью iOS-приложений?

«На что вы обращаете внимание при код-ревью iOS-приложений?» — вопрос из категории Софт-скиллы, который задают на 22% собеседований IOS Разработчик. Ниже — развёрнутый пример ответа, который можно адаптировать под свой опыт.

Ответ

При код-ревью iOS-приложений фокус идёт на качество, безопасность и поддерживаемость кода.

Ключевые аспекты:

  1. Архитектура и дизайн

    • Соблюдение принципов SOLID.
    • Чистое разделение слоёв (MVVM, VIPER, Clean Architecture).
    • Отсутствие Massive View Controller.
    • Правильное использование dependency injection.
  2. Безопасность

    • Чувствительные данные (токены, пароли) хранятся в Keychain, а не в UserDefaults или plist-файлах.
    • Использование HTTPS с правильной конфигурацией SSL/TLS (ATS).
    • Валидация и санитизация пользовательского ввода.
  3. Производительность и память

    • Отсутствие retain cycles (использование weak/unowned для захвата в замыканиях).
    • Выбор оптимальных структур данных (Array vs Set vs Dictionary).
    • Выполнение тяжелых операций (сеть, обработка изображений) в background-очередях.
    • Корректное использование инструментов (Instruments) для поиска утечек.
  4. UI/UX и доступность

    • Корректная работа Auto Layout constraints.
    • Поддержка Dynamic Type для адаптации текста под настройки пользователя.
    • Настройка accessibility-атрибутов (labels, hints, traits).
  5. Тестируемость

    • Код разделён на мелкие, тестируемые модули.
    • Использование протоколов для создания моков в unit-тестах.
    • Наличие покрытия критической бизнес-логики тестами.
  6. Стиль кода и читаемость

    • Следование соглашениям по именованию (Swift API Design Guidelines).
    • Соблюдение принципа DRY (Don't Repeat Yourself).
    • Размер методов и функций (желательно не более 40-50 строк).
    • Читаемость и понятность кода как для текущей команды, так и для будущих разработчиков.

Пример проблемного кода и улучшение:

// ❌ Проблемы: retain cycle, сохранение в UserDefaults, блокирование main queue
func loadData() {
    API.shared.getData { [weak self] result in
        DispatchQueue.main.async {
            self?.data = result
            self?.tableView.reloadData()
            self?.saveToUserDefaults(result) // Небезопасно для чувствительных данных
        }
    }
}
// ✅ Улучшенная версия
func loadData() {
    // 1. Выносим сетевой вызов в отдельный сервис
    dataService.fetchData { [weak self] result in
        switch result {
        case .success(let data):
            // 2. Обработка данных в background queue
            self?.processDataOnBackgroundQueue(data)
        case .failure(let error):
            // 3. Централизованная обработка ошибок
            self?.handleError(error)
        }
    }
}

private func processDataOnBackgroundQueue(_ data: DataModel) {
    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
        let processedData = self?.dataProcessor.transform(data)
        // 4. Сохранение в безопасное хранилище (например, Core Data или Keychain для чувствительных данных)
        self?.secureStorage.save(processedData)
        // 5. Обновление UI только на main queue
        DispatchQueue.main.async {
            self?.updateUI(with: processedData)
        }
    }
}