Для ошибки, которую пользователь может исправить выбором действия, какой контракт Swift позволяет описать варианты восстановления?
Для такой ошибки подходит протокол RecoverableError из Foundation. Он описывает доступные варианты восстановления и метод, который пытается выполнить выбранное действие. Сам протокол не показывает интерфейс и не повторяет операцию автоматически: это обязанность вызывающего кода.
Подход возник из потребности отделить обычное сообщение об ошибке от описания возможных действий пользователя. В модели ошибок Foundation ошибка может содержать не только причину сбоя, но и варианты восстановления, например повторить операцию, использовать другое значение или отменить действие.
Это дополняет базовый механизм throws: throws передаёт факт сбоя, а RecoverableError формализует возможный сценарий восстановления.
Если ошибка содержит только текст, интерфейс вынужден угадывать, какие действия доступны пользователю. Такой код становится хрупким: тексты локализуются, меняются и не должны использоваться как программный контракт.
Если же объявить варианты восстановления, но не определить их реальное поведение, пользователь увидит формально доступное действие, которое ничего не исправляет. Поэтому восстановление должно быть частью согласованного контракта ошибки, а не просто набором подписей кнопок.
Тип ошибки реализует RecoverableError и предоставляет массив recoveryOptions. Эти строки предназначены для отображения пользователю, поэтому их следует локализовать на уровне приложения. Метод attemptRecovery(optionIndex:) получает индекс выбранного варианта, выполняет попытку восстановления и возвращает true, если она завершилась успешно.
Выбрасывание ошибки при этом не меняется: ошибка по-прежнему распространяется через throws и перехватывается в catch. После перехвата вызывающий код сам решает, показывать ли варианты, вызывать ли attemptRecovery, повторять ли исходную операцию и что делать при результате false.
RecoverableError не следует использовать для любого сбоя. Если восстановление требует асинхронного запроса, сложного сценария или участия нескольких слоёв приложения, часто лучше оставить ошибку обычной и реализовать процесс восстановления отдельно. Протокол особенно уместен, когда ошибка действительно может предложить конечный набор непосредственных действий.
Его можно сочетать с LocalizedError для локализованного описания причины. При этом errorDescription отвечает за объяснение сбоя, а recoveryOptions и attemptRecovery — за варианты исправления.
При импорте файла обнаружен дубликат. Рассматривались три варианта: зашить текстовые кнопки в экран, вернуть обычную ошибку без описания восстановления или реализовать RecoverableError.
Первый вариант прост, но связывает слой импорта с конкретным интерфейсом и ломается при локализации. Второй сохраняет разделение слоёв, но заставляет экран самостоятельно знать все причины и допустимые действия.
Выбран третий вариант: доменная ошибка предоставляет варианты восстановления, а экран решает, как их показать. После выбора экран вызывает восстановление и при успехе повторяет или продолжает импорт. В результате причина ошибки и сценарий восстановления остаются типизированными, а UI не анализирует текст сообщения.
Нет. Соответствие протоколу только предоставляет данные и операцию восстановления. Конкретный экран, обработчик ошибок или инфраструктурный компонент должен получить ошибку, построить интерфейс и вызвать метод для выбранного варианта.
Нет, протокол задаёт индекс относительно массива recoveryOptions, но отображение может быть другим. UI не должен безоговорочно предполагать, что порядок вариантов или их количество неизменны: он должен использовать предоставленный массив и передавать корректный индекс выбранного элемента.
Это означает, что попытка восстановления не удалась. Ошибка не исчезает автоматически, исходная операция не повторяется сама и новый throw не возникает по волшебству. Вызывающий код должен решить, сообщить ли о неудаче, предложить другой вариант, повторить операцию или завершить сценарий.