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.
Related
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.)
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)
}
})
I got confused error handling in swift3. I try to do like "if XX function got error then try YY function"
Let me show you what I try:
class MyClass {
enum error: Error
{
case nilString
}
func findURL() {
do {
let opt = try HTTP.GET(url_adr!)
opt.start { response in
if let err = response.error {
print("error: \(err.localizedDescription)")
return //also notify app of failure as needed
}
do
{
/* This is func1. and got error. I want to if this function has error then go next function. */
try self.stringOperation(data: response.description)
}
catch{
print("doesn't work on func1. trying 2nd func")
self.stringOperation2(data:response.descritption)
}
}
} catch let error {
print("got an error creating the request: \(error)")
}
}
func stringOperation(data:String)throws -> Bool{
do{
/** 1 **/
if let _:String = try! data.substring(from: data.index(of: "var sources2")!){
print("its done")
}else{
throw error.nilString
}
IN 1: I got this fatal error on this line:
"fatal error: unexpectedly found nil while unwrapping an Optional value" and program crashed.
I googled error handling try to understand and apply to in my code. However not succeed yet. Can someone explain where did I wrong?
Additional info: I got String extension for .substring(from:...) , and .index(of:"str"). So these lines doesn't got you confused.
As a general rule, try avoiding using force unwrapping (!), where you have
if let _: String= try! data.substring...
Instead use
if let index = data.index(of: "var sources2"),
let _: String = try? data.substring(from: index) { ... } else { ... }
That way you remove the two force unwraps that may be causing your crash. You already have the if let protection for catching the nil value, so you can make the most of it by using the conditional unwrapping.
I am working on a piece of code that is going to fetch an array of NSManagedObjects from CoreData. When using a do catch statement in my code it doesn't seem right to do it this way, but it is the simplest way I can write this line of code.
In any other scenario when you use the return statement you are jumping out of the current function you are in. And you can be assured that no other code in your function will execute past the return statement. I am wondering if the same applies to Swift's do catch paradigm.
class func getAll() -> [MMNotification] {
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<MMNotification>(entityName: "MMNotification")
do {
return try context.fetch(fetchRequest)
}
catch {
// Will this 'catch' if the try fails,
// even if we said we are 'return'ing right before the 'try'?
return []
}
}
Here I am fetching a list of notifications stored in CoreData. In the do block you can see the line of code in question.
QUESTION
Will the catch block execute if the try fails after already stating that the function should return?
What you have should work as expected. Basically what happens is if a throw occurs at any time within a do, the catch is called and any code after the throw will not be executed.
Yes, the catch block will execute if the try in return try fails. The return will not happen.
Here's a little code to prove it to yourself. Paste it into a new playground to try it out.
import UIKit
let shouldFail = true
enum DemoError:Error {
case shouldFail
}
func failableGetter() throws -> String {
if shouldFail { throw DemoError.shouldFail }
return "Succeeded"
}
func fetchInfo() -> String {
do {
return try failableGetter()
} catch {
return "Failed"
}
}
print(fetchInfo()) // "Failed" or "Succeeded" depending on shouldFail
When shouldFail is true, the failableGetter() throws an error and the do-catch in fetchInfo() skips to the catch section before returning.
When shouldFail is false, the failableGetter() doesn't fail and fetchInfo() returns the result.
Adding to this answer. Scope matters a bit here. Code inside the do block code after a throw will NOT be executed. However, code further down outside of the scope of the do block will be executed. I made a simple playground you can run to see for yourself.
import Foundation
let error = NSError(domain: "", code: 123, userInfo: [NSLocalizedDescriptionKey: "My error"])
func functionThatAlwaysThrows() throws {
throw(error)
}
func myFunction() {
do {
try functionThatAlwaysThrows()
// This will never print
print("Continuing after throw inside do scope")
} catch let err {
print("Caught Error: \(err.localizedDescription)")
}
// This will always print
print("Continuing after throw outside do scope")
}
Output:
Caught Error: My error
Continuing after throw outside do scope
If you want more information on Error handling you can take a look at the docs
I've been looking up how to use the guard keyword in Swift. Recently a developer told me that the code below will print "success" if there's no error in the closure.
for attachment in attachments! {
attachment.fetchData { (data, error) in
guard let error = error else {
print(“success”)
return
}
print(error.localizedDescription)
}
I'm a bit confused by his statement. After reading the closure and guard keyword documentation from Apple, it looks to me like his code will print out "success" only when there is an error.
I feel like he's using it in reverse, but I may be wrong. Can someone break it down for me and explain if success is printed when there is or is not an error?
Thank you.
The use of guard to unwrap the error is very misleading. You should use it to unwrap your data and make sure there is no error and provide an early exit to your method in case of error.
Just change your guard statement to:
guard let data = data, error == nil else {
print(error ?? "")
return
}