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

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.)

Related

guard statement in Xcode 13 Why does it will never be executed?

I have the following code:
private func getDate() async throws -> CurrentDate? {
guard let url = URL(string: "someURL") else {
throw fatalError("URL is incorrect!")
}
On the line throw fataError, the compiler keeps saying "This will never be executed". Why is this? According to my understanding if the URL is not formed correctly then it will never bypass the guard and go to else throwing the error.
You can't throw a fatalError. Calling fatalError stops the execution of your program, it crashes it.
You should create a custom error and throw that instead.

Catching RealmSwift write errors i.e. delete

How exactly do I catch RealmSwift add/delete errors?
i.e.
do {
try realm.write {
realm.delete(MyRealmObject())
}
completion(true)
} catch {
completion(false)
}
In this example I've deliberately tried deleting a RealmObject that I've just made up (to make it fail), but it doesn't catch, instead I get this error:
Terminating app due to uncaught exception 'RLMException', reason: 'Can only delete an object from the Realm it belongs to.'
I haven't seen any examples of people handling specific delete/add errors - is there a reason for this?
Not sure if this is what you're looking for but the following code catches and identifies specific add errors. However, errors accessing different instances of realm are handled differently. So starting with this...
class MyObject: Object {
#objc dynamic msg = ""
}
do {
let realm = try Realm()
let a0 = MyObject()
a0.msg = "Hello, World"
try! realm.write {
realm.add(a0)
}
} catch let error as NSError {
print(error.localizedDescription)
}
to then expand.... do the following to ensure the object being deleted matches the realm you are deleting it from.
do {
let realm = try Realm()
if a0.realm == realm {
try! realm.write {
realm.delete(a0)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
Realm developers extend NSError class with their custom description Can only delete an object from the Realm it belongs to and do FatalError in the predefined list of errors they expect ( may be for a good use of the their library as a help to the developer to correctly use it ) , so they intentionally don't throw an exception that the try can catch

How to handler an error of MKDirectionsRequest

I'm not very familiar with error handling and so any advice would be really appreciated.
My code makes multiple calls recursively to the Apple API for calculating a route as it needs to calculate the distance for multiple options.
do {
directions.calculate(completionHandler: {(response, error) in
let response = (response?.routes)! //This line bugs out
for route in response {
//code
completion(result, error)
}
})
}
catch {
print(error.localizedDescription)
}
It tends to crash on the 5th or 6th time, and I wondered if there was a way to stop the application crashing and notify instead.
Thanks
There's no point in using a do-catch block, since there's no throwable function in your code, so you won't be able to catch any errors. In Swift you can only catch errors thrown by a function marked throws, all other errors are unrecoverable.
You should safely unwrap the optional response, since it might be nil, in which case the force unwrapping would cause an unrecoverable runtime error that you have already been experiencing.
You can use a guard statement and optional binding to safely unwrap the optional response and exit early in case there's no response.
directions.calculate(completionHandler: {(response, error) in
guard let response = response, error == nil else {
completion(nil,error)
return
}
for route in response.routes {
....
completion(result, nil)
}
})

Get error description from caught error

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))")
}

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)")
}