trying first time to play around with error handling and error concepts. So I found this approach to create Error enums. Conforming to LocalizedError protocol I expected to have the failureReason as further explanation. Following is the enum for doing this.
enum NetworkError : LocalizedError {
case invalidInput(String)
case unexpected
case invalidURL(_ url: URL)
case apiError(statusCode: Int)
var faliureReason: String? {
switch self {
case .invalidInput:
return "Request body is nil."
case .unexpected:
return "..."
case .invalidURL(_):
return "..."
case .apiError(statusCode: let statusCode):
return "..."
}
}
}
in my caller function I've an catch block for this but the error.failureReason property remains nil.
func updateItem() async {
guard let car = car else { return }
let resource = PostCarResource(car: car)
let request = APIRequest(resource: resource)
var errorMessage = nil
do {
try await request.execute()
} catch let error as LocalizedError {
self.errorMessage = error.localizedDescription
print(".")
print("self.errorMessage")
print(self.errorMessage)
print(".")
print("error.failureReason")
print(error.failureReason ?? "no Reason")
print(".")
print("error")
print(error)
print(".")
print("dump")
dump(error)
} catch {
dump(error)
}
}
I'd expect that after the error has been set correctly to .invalidInput (what happens correctly) I could use error.failureReason but this is not the case. So why I'm not getting the String "Request body is nil."
The output of my console looks like:
.
self.errorMessage
Optional("The operation couldn’t be completed. (CarUSell.NetworkError error 0.)")
.
error.failureReason
no Reason
.
error
invalidInput("Request body is nil.")
.
dump
▿ -.NetworkError.invalidInput
- invalidInput: "Request body is nil."
Looks like a simple typo. Your code defines
var faliureReason
That is not the same as failureReason
. That's all, really.
However, although this is not quite what you asked, note that you are using LocalizedError incorrectly. You do not need to catch a LocalizedError in a special way; it is an Error. LocalizedError is merely a way of constructing the error. So instead of
} catch let error as LocalizedError {
You should should just be saying
} catch {
The error will arrive as an Error called error
, so now just read that. If you want to check whether it has a failure reason, cast to an NSError and check the localizedFailureReason
:
} catch {
print((error as NSError).localizedFailureReason)
}
Here is a complete playground example:
enum NetworkError : LocalizedError {
case invalidInput(String)
case unexpected
case invalidURL(_ url: URL)
case apiError(statusCode: Int)
var failureReason: String? {
switch self {
case .invalidInput:
return "Request body is nil."
case .unexpected:
return "..."
case .invalidURL(_):
return "..."
case .apiError(statusCode: _):
return "..."
}
}
}
func test() {
do {
throw NetworkError.invalidInput("what")
} catch {
print((error as NSError).localizedFailureReason) // "Request body is nil"
}
}
test()
Observe too that you have defined invalidInput(String)
but you are not using the String in your failureReason
. So I didn't use it either (but I had to supply a dummy string, when throwing, in order to get the example to compile). You probably meant something like this (I've marked the key changes with comments):
enum MyNetworkError : LocalizedError {
case invalidInput(String)
case unexpected
case invalidURL(_ url: URL)
case apiError(statusCode: Int)
var failureReason: String? {
switch self {
case .invalidInput(let message): // <--
return message // <--
case .unexpected:
return "..."
case .invalidURL(_):
return "..."
case .apiError(statusCode: _):
return "..."
}
}
}
func test() {
do {
throw MyNetworkError.invalidInput("Request body is nil") // <--
} catch {
print((error as NSError).localizedFailureReason) // "Request body is nil"
}
}
test()