Avatar ·

Предупреждение "Expression of type UIViewController? is unused" в Xcode 8/Swift 3

Всем привет! Я новичок в Swift и столкнулся с непонятным предупреждением в Xcode 8 при работе с iOS 10. У меня есть простой код, который показывает контроллер через storyboard: ```swift let storyboard = UIStoryboard(name: "Main", bundle: nil) let secondVC = storyboard.instantiateViewController(withIdentifier: "SecondViewController") self.present(secondVC, animated: true, completion: nil) ``` Всё работает, но Xcode выдаёт жёлтое предупреждение на второй строке: "Expression of type 'UIViewController?' is unused". Я понимаю, что это предупреждение о неиспользуемом значении, но ведь я использую secondVC в следующей строке! Что я делаю не так? Раньше на Swift 2 такого не было. Как правильно писать этот код без предупреждения? Пробовал добавить восклицательный знак после instantiateViewController, но ничего не изменилось. Может быть, проблема в том, что метод возвращает опциональный тип? Помогите разобраться, пожалуйста. <<>>

Причина предупреждения

В Swift 3 метод instantiateViewController(withIdentifier:) начал возвращать UIViewController? (опциональный тип) вместо UIViewController (неопционального). Предупреждение возникает, потому что вы присваиваете результат переменной secondVC, но компилятор видит, что это опциональное значение не используется напрямую — оно просто передаётся в present. Фактически, вы могли бы написать present(storyboard.instantiateViewController(...), ...) без промежуточной переменной, и предупреждение исчезло бы. Однако правильное решение — безопасно развернуть опционал.

Основное решение: использование guard let или if let

Лучший способ избавиться от предупреждения и сделать код безопасным — использовать опциональное связывание:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let secondVC = storyboard.instantiateViewController(withIdentifier: "SecondViewController") else {
    fatalError("Не удалось загрузить SecondViewController из storyboard")
}
self.present(secondVC, animated: true, completion: nil)

Если вы уверены, что контроллер точно существует, можно использовать принудительное развёртывание с !:

let secondVC = storyboard.instantiateViewController(withIdentifier: "SecondViewController")!
self.present(secondVC, animated: true, completion: nil)

Но это опасно — если идентификатор указан неверно, приложение упадёт. Лучше использовать guard.

Почему это работает?

  • Опциональное связывание — компилятор видит, что вы обработали случай nil, и предупреждение исчезает.
  • Принудительное развёртывание — вы явно говорите: "я знаю, что это не nil", и компилятор перестаёт беспокоиться.
  • В Swift 2 метод возвращал AnyObject, поэтому предупреждения не было. В Swift 3 тип стал строже.

Дополнительный совет: используйте обобщённые методы

Для удобства можно создать расширение:

extension UIStoryboard {
    func instantiateViewController<T: UIViewController>(ofType type: T.Type, identifier: String) -> T? {
        return instantiateViewController(withIdentifier: identifier) as? T
    }
}

// Использование:
guard let secondVC = storyboard.instantiateViewController(ofType: SecondViewController.self, identifier: "SecondViewController") else {
    return
}
self.present(secondVC, animated: true, completion: nil)

Этот подход гарантирует, что вы получите контроллер правильного типа, и полностью устраняет предупреждение.

<<>>

Альтернативный подход: использование as? с приведением типа

Если вы точно знаете тип контроллера, можно сделать приведение прямо в строке с презентацией, что также убирает предупреждение:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let secondVC = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
    self.present(secondVC, animated: true, completion: nil)
} else {
    print("Не удалось получить SecondViewController")
}

Этот вариант решает две проблемы сразу: убирает предупреждение о неиспользуемом выражении и даёт типобезопасный доступ к свойствам контроллера (например, secondVC.someProperty).

Важно: Если вам не нужен специфический тип, а достаточно общего UIViewController, используйте guard let из первого ответа. Если же вы хотите сразу работать с конкретным подклассом — as? будет лучшим выбором.

Также можно подавить предупреждение явно, присвоив результат _:

let _ = storyboard.instantiateViewController(withIdentifier: "SecondViewController")

Но это плохая практика — вы теряете контроль над опциональностью и не обрабатываете возможную ошибку.