UIAlertView в Swift: 5 фатальных ошибок и как их избежать

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему ваш UIAlertView не работает (и при чём тут Stack Overflow)

Если вы когда-нибудь гуглили «How would I create a UIAlertView in Swift?» — вы не одиноки. Этот вопрос на Stack Overflow собрал сотни тысяч просмотров. Кажется, что может быть проще? Показать пользователю всплывающее окно с кнопкой «ОК». Но на практике новички (и не только) наступают на одни и те же грабли.

В этой статье мы разберём топ-5 ошибок при создании UIAlertView в Swift, покажем, как их исправить, и дадим готовый код для UIAlertController — современной замены устаревшему API. Если вы хотите писать чистый, работающий код без крашей — оставайтесь с нами.

Ошибка №1: Использование UIAlertView вместо UIAlertController

Самая распространённая ошибка — попытка вызвать UIAlertView в современном Swift. Этот класс был объявлен устаревшим (deprecated) ещё в iOS 8. Если вы пишете:

let alert = UIAlertView(title: "Ошибка", message: "Что-то пошло не так", delegate: nil, cancelButtonTitle: "OK")
alert.show()

…то компилятор, скорее всего, выдаст предупреждение или ошибку. А если проект соберётся — в лучшем случае алерт не будет работать корректно на iOS 13+.

Почему так происходит?

Apple заменила UIAlertView на UIAlertController ещё в iOS 8. Старый класс больше не поддерживается, и его использование может привести к неожиданному поведению.

Как исправить?

Используйте UIAlertController с предпочтительным стилем .alert или .actionSheet. Вот правильный код:

let alert = UIAlertController(title: "Ошибка",
                              message: "Что-то пошло не так",
                              preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)

Запомните: в современном Swift UIAlertView — это legacy. Всегда используйте UIAlertController.

Ошибка №2: Забыли вызвать present()

Даже если вы создали UIAlertController, но забыли его показать — ничего не произойдёт. Буквально. Код выполнится, алерт будет создан в памяти, но пользователь его не увидит.

Типичный пример ошибки:

func showAlert() {
    let alert = UIAlertController(title: "Привет", message: "Мир", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .default))
    // Забыли present!
}

Результат: тишина. Никакой ошибки, никакого алерта. Вы будете гадать, почему код не работает.

Как исправить?

Всегда вызывайте present(_:animated:completion:) на текущем view controller:

func showAlert() {
    let alert = UIAlertController(title: "Привет", message: "Мир", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .default))
    present(alert, animated: true, completion: nil)
}

Совет: если вы вызываете алерт из замыкания или асинхронного кода, убедитесь, что self (view controller) ещё существует. Иначе получите crash.

Ошибка №3: Неправильная обработка нажатия кнопок

В старом UIAlertView мы использовали делегат alertView(_:clickedButtonAt:). В UIAlertController всё иначе — используются замыкания (closures).

Новички часто пытаются подписать класс на UIAlertViewDelegate и реализовать методы делегата, но это не сработает с UIAlertController.

Неправильно:

class ViewController: UIViewController, UIAlertViewDelegate {
    func showAlert() {
        let alert = UIAlertController(title: "Вопрос", message: "Вы согласны?", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Да", style: .default, handler: nil))
        // Где обработка?!
        present(alert, animated: true)
    }
}

Правильно — используем handler:

func showAlertWithAction() {
    let alert = UIAlertController(title: "Вопрос", message: "Вы согласны?", preferredStyle: .alert)
    
    let yesAction = UIAlertAction(title: "Да", style: .default) { _ in
        print("Пользователь согласился")
        // Здесь ваш код
    }
    let noAction = UIAlertAction(title: "Нет", style: .cancel) { _ in
        print("Пользователь отказался")
    }
    
    alert.addAction(yesAction)
    alert.addAction(noAction)
    present(alert, animated: true)
}

Важно: каждый UIAlertAction может иметь свой обработчик. Не нужно отдельного делегата.

Ошибка №4: Добавление действий после present()

Ещё одна коварная ошибка — модификация алерта после того, как он уже показан. UIAlertController не поддерживает динамическое добавление кнопок после вызова present.

Пример ошибки:

let alert = UIAlertController(title: "Загрузка", message: "Подождите...", preferredStyle: .alert)
present(alert, animated: true)

// Через 2 секунды пытаемся добавить кнопку
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    alert.addAction(UIAlertAction(title: "OK", style: .default)) // ОШИБКА!
}

Это вызовет исключение: “Attempt to add action to alert view after it has been presented”.

Как исправить?

Добавляйте все действия до вызова present. Если нужно показать алерт с задержкой — создайте новый экземпляр:

// Показываем алерт загрузки
let loadingAlert = UIAlertController(title: "Загрузка", message: "Подождите...", preferredStyle: .alert)
present(loadingAlert, animated: true)

// Через 2 секунды скрываем и показываем новый
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    loadingAlert.dismiss(animated: true) {
        let successAlert = UIAlertController(title: "Готово", message: "Загрузка завершена", preferredStyle: .alert)
        successAlert.addAction(UIAlertAction(title: "OK", style: .default))
        self.present(successAlert, animated: true)
    }
}

Запомните: алерт — это конечный автомат. После показа его нельзя изменить.

Ошибка №5: Проблемы с потоками (threading)

UIKit не является потокобезопасным. Все операции с UI должны выполняться на главном потоке (main thread). Если вы попытаетесь показать алерт из фонового потока — приложение может упасть.

Типичный сценарий:

DispatchQueue.global().async {
    // Какой-то тяжёлый код
    let alert = UIAlertController(title: "Ошибка", message: "Сеть недоступна", preferredStyle: .alert)
    alert.add                

Блоги

Рекомендаций по книгам