How to provide a localized description with an Error type in Swift?


Swift provides a protocol to achieve the localized description. You can use the LocalizedError protocol to provide a localized description of an error type. This protocol can be conformed by structure, enum, or class. After adopting this protocol, you have to implement the errorDescription property to provide the localized description.

Here is an example of a custom error enum that conforms to the LocalizedError protocol −

Create a custom error and conform to the Error type

In this example, we will create an enum called CustomError to conform to the LocalizedError protocol. In the enum, we will add cases to define the different types of errors. Each error will represent a different error. Here is the example of creating an enum with an Error type −

Example 

import Foundation
// An enum with different error types.
enum CustomError: Error {
   case invalidUrl
   case httpError
   case timeoutError
   case noConnectionError
   case encodingError
   case decodingError
   case invalidResponseError
   case unexpected(code: Int)
}

In the above code, we created an enum called CustomError with Error type. The same enum we will use to conform to the different protocols to localize the description.

Conforms to the CustomStringConvertible Protocol

First, we will see an example with the CustomStringConvertible protocol. Using this, you can provide a custom message for each type of error. To provide a custom message, you can override the description property.

Example 

Here is an example to extend the CustomError enum by conforming to the CustomStringConvertible protocol −

// Assign an appropriate description to each type of error.
extension CustomError: CustomStringConvertible {
    
   var description: String {
      switch self {
         case .invalidUrl:
            return "There is a problem with a URL, such as an invalid URL or a timeout."
            
         case .httpError:
            return "There is an issue with an HTTP request or response."
            
         case .timeoutError:
            return "The request takes longer than the specified timeout period to complete."
            
         case .noConnectionError:
            return "There is no internet connection or the device is offline."
            
         case .encodingError:
            return "The data received from the server is unable to be encoded as the expected type."
            
         case .decodingError:
            return "The data received from the server is unable to be decoded as the expected type."
            
         case .invalidResponseError:
            return "The server responds with an unexpected format or status code."
            
         case .unexpected(let code):
            return "There is an unexpected error with the code \(code)"
      }
   }
}

Add localizedDescription for custom errors using LocalizedError

In this step, you will see how to conform to the LocalizedError protocol to provide the localized descriptions for each error type. Once you conform to the LocalizedError protocol, you can override the errorDescription property to provide localization.

Example 

Here is an example to extend the CustomError enum with LocalizedError protocol −

// Assign a localized description to each type of error.
extension CustomError: LocalizedError {
    
   var errorDescription: String? {
      switch self {
         case .invalidUrl:
            return NSLocalizedString("There is a problem with a URL, such as an invalid URL or a timeout.", comment: "Invalid URL")
            
         case .httpError:
            return NSLocalizedString("There is an issue with an HTTP request or response.", comment: "HTTP Error")
            
         case .timeoutError:
            return NSLocalizedString("The request takes longer than the specified timeout period to complete.", comment: "Timeout Error")
            
         case .noConnectionError:
            return NSLocalizedString("There is no internet connection or the device is offline.", comment: "Internet Connection Error")
            
         case .encodingError:
            return NSLocalizedString("The data received from the server is unable to be encoded as the expected type.", comment: "Encoding Error")
            
         case .decodingError:
            return NSLocalizedString("The data received from the server is unable to be decoded as the expected type.", comment: "Decoding Error")
            
         case .invalidResponseError:
            return NSLocalizedString("The server responds with an unexpected format or status code.", comment: "Invalid Response Error")
            
         case .unexpected(let code):
            return NSLocalizedString("There is an unexpected error with the code \(code)", comment: "Unexpected Error")
      }
   }
}

Extending Custom Error

In Swift, an enum has more power that provides you to extend its functionality. You can add computed properties and functions to an enum.

We will add a computed property to determine whether the error is the fatal type or not. In other words, we will check whether the error can be handled or not. For example −

// Checking for unexpected errors that cannot be handled.
extension CustomError {
   var isFatal: Bool {
      if case CustomError.unexpected = self { return true }
      else { return false }
   }
}

Practical Usage

We are ready with the custom errors with the localized description. Now, let’s see how to use them in your project.

Example 

func checkServerResponse(_ response: [String: Any]?) -> CustomError? {
    
   // check for valid response
   guard let responseDictionary = response else {
      return CustomError.invalidResponseError
   }
    
   // check for valid status code
   guard let statusCode = responseDictionary["statusCode"] as? Int,
      statusCode >= 200 && statusCode <= 300  else {
      return CustomError.unexpected(code: -1002)
   }
    
   // check for valid data object
   guard let dataDictionary = responseDictionary["data"] as? [String: Any] else {
      return CustomError.encodingError
   }
    
   print("Received Data: \(dataDictionary)")
   return nil
}
let response: [String: Any] = ["statusCode": 201, "data": "user name"]
if let error = checkServerResponse(response) {
   print("Error description: \(error.description)\nLocalized Description: \(error.localizedDescription)\nFatal Status: \(error.isFatal)")
}

Output

Error description: The data received from the server is unable to be encoded as the expected type.
Localized Description: The data received from the server is unable to be encoded as the expected type.
Fatal Status: false

In the above example, we created a function to check the response received from the server. In case any error is found, this function returns a custom error type of optional type.

In the function implementation, we check for different valid cases. At each check, return a custom error if something is wrong.

Conclusion

In real applications, most of the time you deal with network requests. It is obvious you have to manage the different types of errors. In Swift, there some protocols which you can conforms and extends the capability to understand the error with custom messages or localized description.

The CustomStringConvertible protocol can be used to provide the custom message by overriding the description property. You can give meaningful message to each error types. The LocalizedError protocol can also be used to provide a localized description of an error type. It's good practice to use this feature to show a user-friendly message when an error occurs.

Updated on: 28-Feb-2023

2K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements