Что происходит при форс анрапе и упадет ли приложение
Что проверяют
Понимаете ли вы, что восклицательный знак это не способ достать значение, а утверждение «здесь nil невозможен». Хороший ответ отличает ситуации, где падение полезно, от тех, где оно означает непродуманный поток данных.
Как отвечать
Если внутри опционала значение, оно возвращается. Если там nil, рантайм срабатывает ловушкой и процесс завершается с сообщением про неожиданно найденный nil. Это не бросаемая ошибка Swift: do/catch её не ловит, try? тоже, восстановиться нельзя.
В ту же семью входят as! при неверном типе, try! на брошенной ошибке и обращение за границу массива. Все они про нарушенный контракт, а не про обработку ошибок.
Безопасные инструменты: if let, guard let с ранним выходом, оператор ?? со значением по умолчанию, опциональная цепочка и map с flatMap на самом опционале.
Отдельно стоит сказать про неявно развёрнутый опционал вида var view: UIView!. Он разворачивается при каждом обращении, поэтому падает не в месте объявления, а при первом использовании, и обращение к аутлету до загрузки представления это типичный случай.
Когда ! уместен: когда nil означает ошибку программиста, а не вариант данных. Ресурс из бандла, аутлет после загрузки, инвариант, который гарантирован выше по коду. Тогда падение случается рядом с причиной, и это лучше, чем тихо пропущенный экран.
Где спотыкаются
Отвечают крайностями: «упадёт всегда» или «ничего не будет». Верный ответ зависит от значения: падение случается только на nil, и весь вопрос в том, гарантирован ли инвариант в этом месте.
Меняют восклицательный знак на вопросительный и считают, что починили. Цепочка с ?. не падает, но и работу не делает: кнопка не нажимается, экран остаётся пустым. Дефект становится тише, а не меньше, поэтому нужен guard с явной обработкой ветки nil.
Пытаются накрыть форс анрап тестом «не падает» или поймать его в do/catch. Ловушку перехватить нельзя, проверять надо не факт падения, а сам инвариант: тест на случай отсутствующего значения и на ветку, которая его обрабатывает.