При добавлении контекста к исходной ошибке в Swift какой способ позволит сохранить её причину для диагностики?
Оберните исходную ошибку в собственный тип ошибки, сохранив её в ассоциированном значении, например underlying: Error. Так внешний слой получает контекст операции, а внутреннюю причину можно извлечь для диагностики или специальной обработки.
Механизм throws сообщает о факте сбоя, но протокол Error не требует единого поля для исходной причины или контекста. Поэтому при прохождении ошибки через несколько слоёв приложения разработчику нужен явный способ не потерять сведения о месте и причине сбоя.
Для этого обычно применяют оборачивание ошибок: каждый слой добавляет свой контекст, сохраняя предыдущую ошибку внутри. Это не отдельная автоматическая возможность Swift, а соглашение проектирования типов ошибок.
Если заменить исходную ошибку новой без сохранения причины, вызывающий код увидит только общее сообщение вроде ошибки загрузки. Диагностика станет менее точной, а анализ первопричины потребует разбора нестабильного текста сообщения или журналов.
При этом бездумное оборачивание тоже создаёт проблему: обработчик может начать видеть только внешний тип и перестать различать ожидаемые внутренние причины. Поэтому нужно заранее определить, какие ошибки являются частью контракта API, а какие предназначены только для диагностики.
Создайте тип-обёртку с контекстом операции и полем underlying типа Error. Исходную ошибку передайте в это поле, а внешний код используйте для определения уровня, на котором произошёл сбой.
В результате внешний обработчик может распознать ReadConfigurationError и получить путь, а затем при необходимости проверить underlying на конкретный тип или значение. Такая схема сохраняет причинную цепочку, не превращая её в текст.
Важно отличать диагностический контекст от публичного контракта. Если вызывающий код должен программно реагировать на отсутствие файла, соответствующий случай лучше представить стабильным типом или значением публичной ошибки. Внутренние детали реализации не следует без необходимости делать частью API.
Обёртка может быть структурой, если ей нужны произвольные поля, или enum, если набор внешних сценариев ограничен. Недостаток подхода — усложнение цепочки обработки: для поиска глубокой причины может понадобиться последовательное раскрытие underlying. Нельзя рассчитывать на localizedDescription как на стабильный идентификатор ошибки.
Сервис загружает конфигурацию из файла. Низкоуровневый слой сообщает FileError.missing, но слой конфигурации должен добавить имя файла и обозначить, что не удалось прочитать именно конфигурацию.
Вариант с заменой ошибки на общее ConfigurationError.invalid прост, но теряет причину и затрудняет диагностику. Передача исходной ошибки без контекста сохраняет причину, однако не сообщает, какой файл или операция были затронуты. Разбор текста ошибки сохраняет видимость контекста, но хрупок: текст может измениться из-за локализации или реализации библиотеки.
Выбранное решение — ConfigurationError с полями контекста и underlying: Error. Внешний код обрабатывает стабильный класс сбоя конфигурации, журнал получает путь и исходную причину, а реализация файлового слоя остаётся скрытой от потребителей API.
Можно ли после оборачивания поймать исходную ошибку обычным catch по её типу?
Нет, внешний выброшенный объект имеет тип обёртки. Обработчик catch сначала увидит ReadConfigurationError, поэтому проверка исходного типа напрямую не сработает. Нужно извлечь underlying и отдельно проверить его, либо предоставить в обёртке метод или свойство для классификации причины.
Почему нельзя использовать текст localizedDescription для определения причины?
Текст предназначен для представления пользователю и не является надёжным машинным контрактом. Он может измениться, зависеть от локали или не содержать нужных структурированных данных. Для логики обработки следует использовать типы ошибок, перечисления и их associated values, а текст оставлять для отображения и журналирования.
Когда оборачивание ошибки нарушает границу публичного API?
Оно нарушает её, если публичный потребитель вынужден знать внутренние типы нижнего слоя, чтобы корректно обработать обычный сценарий. Например, API конфигурации не должен требовать от клиента зависимости от конкретной файловой библиотеки. В таком случае наружу публикуют стабильную ошибку верхнего уровня, а исходную ошибку сохраняют внутри только для диагностики или предоставляют через документированный механизм доступа к причине.