Get error description from caught error - swift

I do something like this:
let decoder = JSONDecoder()
do
{
let decodedData = try decoder.decode(type, from: data)
}
catch DecodingError.dataCorrupted
{
let descr = ???
Log.error("Failed to decode JSON response. Error was: \(descr)")
}
how can I access the error description from this? Why can I not simply catch any kind of error in one catch and access its debug description?

How to access the error description
In Swift, a lot of the errors conform to the protocol LocalizedError, which will give you a variable localizedDescription: String? that you can use to print an error message. DecodingError should not be any different.
How to catch any kind of error
You should be able to catch any kind of errors in one catch. In order to do this, you can use
catch let error as DecodingError {
// Any error of type DecodingError
}
or
catch {
// Any possible error
}
Putting it all together
If I understand correctly, you are tring to catch any error of type DecodingError. In that case, you can simply do the following
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(type, from: data)
} catch let error as? DecodingError {
Log.error("Failed to decode JSON response. Error was: \(String(describing: error.localizedDescription))")
}

Related

How to change error.localizedDescription to JSON by Swift?

In Swift I got the error from Cloud Functions.
And I caught the error like this
functions.httpsCallable("addMessage").call(["text": inputField.text]) { result, error in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
}
}
}
The message get like JSON format from Cloud functions.
{"message":"text is required","status":"INVALID_ARGUMENT"}
If I got the error message from Cloud functions, and I want to display to the user.
First I tried
let status = message["status"]
But I got No exact matches in call to subscript in xcode.
How do I resolve this problem?
error.localizedDescription is obviously a JSON string, you have to deserialize it
let messageJSON = error.localizedDescription
let messageDictionary = try? JSONDecoder().decode([String:String].self, from: Data(messageJSON.utf8))
let message = messageDictionary?["message"] ?? ""

Why try/catch block doesn't cover all the runtime exceptions?

What I try to do is
...
let path: URL? = URL(string: "")
do {
let data = try Data(contentsOf: path!)
}
catch {
print("ERROR: \(error)")
}
...
I know that this code won't work, but what I expect is to catch the error in the catch block instead I get a runtime exception and crash.
try this
..
if let path = URL(string: "someStringForUrl") {
do {
let data = try Data(contentsOf: path)
}
catch {
print("ERROR: \(error)")
}
}
...
You never reach the try, so you never reach the catch. Before any of that can happen, the path! forced unwrap fails and crashes the app.
(Crashing the app is a runtime exception, which is a completely different thing from the Swift try/catch mechanism. It doesn't not "cover all the runtime exceptions"; it doesn't cover any runtime exceptions. You're confusing apples with elephants; they are totally unrelated. Runtime exceptions are not thrown-caught Error objects, and vice versa.)

Catch' block is unreachable but I included try

I'm getting the following error:
'catch' block is unreachable because no errors are thrown in 'do' block
But my try is present, how can I include the error then?
AF.request(url).responseData(completionHandler: { data in
do {
if let apiJsonData = try? JSONDecoder().decode(MyModel.self, from: data.data!){
self.items = apiJsonData.data.items
}
} catch {
print("Decoding failed -> ERROR:", error)
}
}
If I include it in the header like: data, error in it throws an error:
Contextual closure type '(AFDataResponse<Data>) -> Void' (aka '(DataResponse<Data, AFError>) -> ()') expects 1 argument, but 2 were used in closure body
How can I have the try catch correctly setup?
You've used try?, not try.
try? turns the whole expression into nil if an error was thrown. It already handles the error on its own, so there's no more error for the catch block to catch. On the other hand, try doesn't do that. For more info, see the Error handling section of the Swift Guide.
If you want the error to be caught by the catch block, you should use try, and delete the if block.
AF.request(url).responseData(completionHandler: { data in
do {
let apiJsonData = try JSONDecoder().decode(MyModel.self, from: data.data!)
self.items = apiJsonData.data.items
} catch {
print("Decoding failed -> ERROR:", error)
}
}
Also note that you are assuming data.data is not nil here. This might not be the case if the network request fails. Better check for it:
AF.request(url).responseData(completionHandler: { data in
do {
guard let data = data.data else {
print("Response Error:", data.error)
return
}
let apiJsonData = try JSONDecoder().decode(MyModel.self, from: data.data!)
self.items = apiJsonData.data.items
} catch {
print("Decoding failed -> ERROR:", error)
}
}

Getting error when trying to use Result type with delegate

Im tring to make a network call and instead of using callback I try to use delegate instead.using Result type where .Sucsess is T: Decodable and .failure is Error. passing my model in the .Sucsess is working but when trying to pass an error I get a compile error "Generic parameter 'T' could not be inferred" what am I missing ?
protocol NetworkServiceDelegate: class {
func decodableResponce<T: Decodable>(_ result: Result<T, NetworkError>)
}
let dataTask:URLSessionTask = session.dataTask(with: url) { (dataOrNil, responceOrNil, errOrNil) in
if let error = errOrNil {
switch error {
case URLError.networkConnectionLost,URLError.notConnectedToInternet:
print("no network connection")
self.delegate?.decodableResponce(Result.failure(.networkConnectionLost))
case URLError.cannotFindHost, URLError.notConnectedToInternet:
print("cant find the host, could be to busy, try again in a little while")
case URLError.cancelled:
// if cancelled with the cancelled method the complition is still called
print("dont bother the user, we're doing what they want")
default:
print("error = \(error.localizedDescription)")
}
return
}
guard let httpResponce:HTTPURLResponse = responceOrNil as? HTTPURLResponse
else{
print("not an http responce")
return
}
guard let dataResponse = dataOrNil,
errOrNil == nil else {
print(errOrNil?.localizedDescription ?? "Response Error")
return }
do{
//here dataResponse received from a network request
let decoder = JSONDecoder()
let modelArray = try decoder.decode([Movie].self, from:
dataResponse) //Decode JSON Response Data
DispatchQueue.main.async {
self.delegate?.decodableResponce(Result.success(modelArray))
}
} catch let parsingError {
print("Error", parsingError)
}
print("http status = \(httpResponce.statusCode)")
print("completed")
}
this line generates the error, it dosnt metter if I pass my enum that cumfirms to Error or trying to pass the error from the dataTask
self.delegate?.decodableResponce(Result.failure(.networkConnectionLost))
Well, you have two problems, having to do with the question "what type is this?" Swift is very strict about types, so you need to get clear about that.
.networkConnectionLost is not an Error. It is an error code. You need to pass an Error object to a Result when you want to package up the error. For example, URLError(URLError.networkConnectionLost) is an Error.
The phrase Result<T, NetworkError> makes no sense. Result is already a generic. Your job is to resolve the generic that it already is. You do that by specifying the type.
So for example, you might declare:
func decodableResponce(_ result: Result<Decodable, Error>)
It is then possible to say (as tests):
decodableResponce(.failure(URLError(URLError.networkConnectionLost)))
or (assuming Movie is Decodable):
decodableResponce(.success([Movie()]))
That proves we have our types right, and you can proceed to build up your actual code around that example code.

Error Handling in Swift 3

I'm migrating my code over to Swift 3 and see a bunch of the same warnings with my do/try/catch blocks. I want to check if an assignment doesn't return nil and then print something out to the console if it doesn't work. The catch block says it "is unreachable because no errors are thrown in 'do' block". I would want to catch all errors with one catch block.
let xmlString: String?
do{
//Warning for line below: "no calls to throwing function occurs within 'try' expression
try xmlString = String(contentsOfURL: accessURL, encoding: String.Encoding.utf8)
var xmlDict = XMLDictionaryParser.sharedInstance().dictionary(with: xmlString)
if let models = xmlDict?["Cygnet"] {
self.cygnets = models as! NSArray
}
//Warning for line below: "catch block is unreachable because no errors are thrown in 'do' block
} catch {
print("error getting xml string")
}
How would I write a proper try catch block that would handle assignment errors?
One way you can do is throwing your own errors on finding nil.
With having this sort of your own error:
enum MyError: Error {
case FoundNil(String)
}
You can write something like this:
do{
let xmlString = try String(contentsOf: accessURL, encoding: String.Encoding.utf8)
guard let xmlDict = XMLDictionaryParser.sharedInstance().dictionary(with: xmlString) else {
throw MyError.FoundNil("xmlDict")
}
guard let models = xmlDict["Cygnet"] as? NSArray else {
throw MyError.FoundNil("models")
}
self.cygnets = models
} catch {
print("error getting xml string: \(error)")
}