Как добавить локализованное описание для ошибок в Swift (Error протокол)
enum MyError: Error {
case networkError
case invalidInput(String)
case unknown
}
Я хочу, чтобы при возникновении ошибки пользователь видел понятное сообщение на его языке. Я знаю про NSLocalizedString, но не понимаю, как правильно интегрировать её с Error. Пробовал сделать так:
extension MyError: LocalizedError {
var errorDescription: String? {
switch self {
case .networkError:
return NSLocalizedString("Ошибка сети", comment: "Network error")
case .invalidInput(let field):
return NSLocalizedString("Неверный ввод в поле \(field)", comment: "Invalid input")
case .unknown:
return NSLocalizedString("Неизвестная ошибка", comment: "Unknown error")
}
}
}
Но когда я ловлю ошибку в do-catch и печатаю error.localizedDescription, получаю просто "The operation couldn’t be completed." без моего текста. Что я делаю не так? Как правильно заставить Swift использовать моё локализованное описание? Нужно ли что-то дополнительно настраивать в проекте для работы с NSLocalizedString? Заранее спасибо за помощь!
<<Правильная реализация локализованных ошибок через протокол LocalizedError
Вы почти всё сделали правильно, но есть одна важная деталь. Протокол LocalizedError требует, чтобы ваша ошибка была классом или структурой, а не просто перечислением. Однако в Swift перечисления могут соответствовать протоколам, и ваша реализация errorDescription верна. Проблема в том, что вы используете error.localizedDescription в неправильном контексте.
Давайте разберёмся по шагам. Когда вы ловите ошибку в catch, Swift автоматически вызывает localizedDescription из протокола Error. Но базовая реализация Error не знает о вашем errorDescription. Чтобы это работало, нужно явно привести ошибку к типу LocalizedError или использовать localizedDescription из протокола LocalizedError.
Вот правильный подход:
enum MyError: LocalizedError {
case networkError
case invalidInput(String)
case unknown
var errorDescription: String? {
switch self {
case .networkError:
return NSLocalizedString("Ошибка сети. Проверьте подключение к интернету.", comment: "Network error description")
case .invalidInput(let field):
return NSLocalizedString("Неверный ввод в поле: \(field). Пожалуйста, проверьте данные.", comment: "Invalid input error")
case .unknown:
return NSLocalizedString("Произошла неизвестная ошибка. Попробуйте позже.", comment: "Unknown error")
}
}
var failureReason: String? {
switch self {
case .networkError:
return NSLocalizedString("Сервер недоступен", comment: "Network failure reason")
case .invalidInput:
return NSLocalizedString("Некорректные данные", comment: "Invalid input reason")
case .unknown:
return NSLocalizedString("Неизвестная причина", comment: "Unknown failure reason")
}
}
var recoverySuggestion: String? {
switch self {
case .networkError:
return NSLocalizedString("Проверьте Wi-Fi или мобильные данные.", comment: "Network recovery suggestion")
case .invalidInput:
return NSLocalizedString("Введите корректные значения.", comment: "Invalid input recovery")
case .unknown:
return NSLocalizedString("Перезапустите приложение.", comment: "Unknown recovery")
}
}
}
Теперь, когда вы ловите ошибку, используйте:
do {
try someFunction()
} catch let error as LocalizedError {
// Используем error.errorDescription, failureReason, recoverySuggestion
print(error.errorDescription ?? "Неизвестная ошибка")
print(error.failureReason ?? "")
print(error.recoverySuggestion ?? "")
} catch {
// Для ошибок, не реализующих LocalizedError
print(error.localizedDescription)
}
Или ещё проще — используйте localizedDescription напрямую, если вы реализовали errorDescription:
do {
try someFunction()
} catch {
// Swift автоматически вызовет errorDescription, если ошибка соответствует LocalizedError
print(error.localizedDescription)
}
Важные моменты:
- Не забудьте добавить файлы Localizable.strings для каждого поддерживаемого языка. NSLocalizedString будет искать ключи именно там.
- Используйте комментарии в NSLocalizedString — они помогают переводчикам понять контекст.
- Проверьте настройки проекта: в Build Settings > Localization должны быть добавлены нужные языки.
- Для сложных ошибок можно реализовать все три свойства (errorDescription, failureReason, recoverySuggestion) — это даст пользователю максимально полную информацию.
Если после всех исправлений у вас всё равно выводится стандартное сообщение "The operation couldn’t be completed", убедитесь, что ваша ошибка действительно соответствует протоколу LocalizedError. Проверьте через if let localizedError = error as? LocalizedError.
Альтернативный подход: использование структуры вместо enum
Если вам не нравится работать с перечислениями, можно создать структуру, которая будет хранить всю информацию об ошибке и сразу предоставлять локализованное описание.
struct AppError: LocalizedError {
let message: String
let reason: String?
let suggestion: String?
var errorDescription: String? {
return NSLocalizedString(message, comment: "Error description")
}
var failureReason: String? {
return reason.map { NSLocalizedString($0, comment: "Failure reason") }
}
var recoverySuggestion: String? {
return suggestion.map { NSLocalizedString($0, comment: "Recovery suggestion") }
}
// Удобные инициализаторы для частых случаев
static func networkError() -> AppError {
return AppError(
message: "Ошибка сети",
reason: "Сервер недоступен",
suggestion: "Проверьте подключение к интернету"
)
}
static func invalidInput(field: String) -> AppError {
return AppError(
message: "Неверный ввод в поле \(field)",
reason: "Некорректные данные",
suggestion: "Введите правильное значение"
)
}
}
// Использование:
func fetchData() throws {
throw AppError.networkError()
}
do {
try fetchData()
} catch let error as AppError {
print(error.errorDescription ?? "")
}
Этот подход более гибкий: вы можете легко добавлять новые типы ошибок без расширения enum, а также передавать дополнительные данные (например, HTTP-код или имя поля). Минус — нужно помнить о создании правильных экземпляров структуры.
Ответов пока нет.
Войдите чтобы оставить ответ