Программирование SwiftОбработка ошибокРазработчик приложений на Swift

Практическая ситуация: доменная ошибка должна отдавать локализованный текст через localizedDescription, сох...

Практическая ситуация: доменная ошибка должна отдавать локализованный текст через localizedDescription, сохраняя при этом типизированный разбор. Какой механизм Swift это обеспечивает?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Для этого тип ошибки должен соответствовать протоколу LocalizedError. Он позволяет предоставить человекочитаемое описание через errorDescription, а исходный тип ошибки при этом сохраняется для разбора в catch или сравнений по вариантам enum.

Исторический контекст

Базовый протокол Error описывает сам факт сбоя, но не задаёт пользовательский текст. Для отделения машинной классификации ошибки от её представления пользователю в Swift предусмотрен специализированный протокол LocalizedError.

Такой подход решает проблему, при которой разработчику пришлось бы разбирать имя типа или формировать текст непосредственно в месте обработки ошибки. Локализованное представление становится частью контракта ошибки, но не заменяет её структурированные данные.

Постановка проблемы

Если ошибка содержит только варианты enum, вызывающий код может надёжно определить причину сбоя, но пользователь получит техническое или слишком общее сообщение. Если же передавать только строку, типизированный разбор причин будет потерян.

Неверно использовать localizedDescription как машинный идентификатор: текст может измениться из-за локали, редакторских правок или отсутствия локализации. Для программной логики нужно сохранять конкретный тип ошибки и его associated values.

Подробное решение

LocalizedError предоставляет несколько необязательных свойств: errorDescription, failureReason, recoverySuggestion и helpAnchor. Наиболее важное для localizedDescriptionerrorDescription; его значение используется как основное локализованное описание ошибки.

import Foundation enum CheckoutError: Error, LocalizedError { case declined var errorDescription: String? { "Платёж отклонён банком" } } let error: Error = CheckoutError.declined print(error.localizedDescription)

Даже если значение хранится через existentialный тип Error, во время выполнения сохраняется его фактический тип и доступно предоставленное им соответствие LocalizedError. Поэтому интерфейс может получить текст через localizedDescription, а доменный слой — обработать CheckoutError.declined типизированно.

Свойство возвращает String?, потому что конкретная ошибка может не предоставлять описание. Для настоящего приложения строку следует получать из локализационных ресурсов, а не жёстко зашивать в тип ошибки. При этом LocalizedError не делает ошибку Equatable, не задаёт код ошибки и не гарантирует уникальность текста.

Ситуация из практики

Слой оплаты возвращает CheckoutError.declined, а экран оплаты должен показать сообщение пользователю. Возможны два варианта: передавать из слоя оплаты готовую строку или передавать типизированную ошибку и формировать текст в каждом экране.

Готовая строка упрощает отображение, но связывает доменный слой с языком и интерфейсом. Формирование текста в каждом экране сохраняет гибкость, но приводит к дублированию и разным сообщениям для одной причины.

Оптимальный вариант — передавать типизированную ошибку, реализующую LocalizedError, а локализованный текст получать через её контракт. В результате бизнес-логика использует конкретные варианты ошибки, интерфейс получает единое описание, а локализацию можно изменять независимо от алгоритма обработки платежа.

Что кандидаты часто упускают

1. Достаточно ли соответствия LocalizedError, чтобы ошибка стала Equatable?

Нет. LocalizedError отвечает только за человекочитаемое представление. Если нужно сравнивать ошибки, конкретный тип должен отдельно соответствовать Equatable, причём сравнение будет доступно для этого конкретного типа, а не для произвольного значения, хранящегося как Error.

2. Сработает ли локализованное описание, если ошибка передана как Error?

Да, при обращении к localizedDescription сохраняется динамический тип значения. Если фактический тип соответствует LocalizedError и предоставляет errorDescription, это описание может быть использовано даже после присваивания значения переменной типа Error. Однако при приведении к Error теряется удобный статический доступ к свойствам конкретного типа и его вариантам.

3. Можно ли использовать localizedDescription как стабильный код ошибки для аналитики или сетевого протокола?

Нет. Это пользовательский текст, зависящий от локализации и формулировок. Для аналитики и обмена между слоями следует использовать типизированные варианты ошибки, отдельные коды или структурированные свойства, а localizedDescription оставлять для отображения человеку.