Swift Do-Try-Catch Syntax

Swift do-try-catch syntax

There are two important points to the Swift 2 error handling model: exhaustiveness and resiliency. Together, they boil down to your do/catch statement needing to catch every possible error, not just the ones you know you can throw.

Notice that you don't declare what types of errors a function can throw, only whether it throws at all. It's a zero-one-infinity sort of problem: as someone defining a function for others (including your future self) to use, you don't want to have to make every client of your function adapt to every change in the implementation of your function, including what errors it can throw. You want code that calls your function to be resilient to such change.

Because your function can't say what kind of errors it throws (or might throw in the future), the catch blocks that catch it errors don't know what types of errors it might throw. So, in addition to handling the error types you know about, you need to handle the ones you don't with a universal catch statement -- that way if your function changes the set of errors it throws in the future, callers will still catch its errors.

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}

But let's not stop there. Think about this resilience idea some more. The way you've designed your sandwich, you have to describe errors in every place where you use them. That means that whenever you change the set of error cases, you have to change every place that uses them... not very fun.

The idea behind defining your own error types is to let you centralize things like that. You could define a description method for your errors:

extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}

And then your error handling code can ask your error type to describe itself -- now every place where you handle errors can use the same code, and handle possible future error cases, too.

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}

This also paves the way for error types (or extensions on them) to support other ways of reporting errors -- for example, you could have an extension on your error type that knows how to present a UIAlertController for reporting the error to an iOS user.

Swift Do Try Catch behaviour

Here are the steps I would do to debug that behavior:

(1) Does your 'jsonparshing(mydata)' really throw?

1.a. You could check the type of the 'jsonParsing(_:)' for the throws keyword.

1.b. Or you could try to omit the 'do { } catch {}' and 'try' altogether to see if Xcode complain that the function throws and should use try.

// btw. is that a typo? Maybe 'jsonParsing(myData)' is more custom.

(2) Assuming you past check in (1). I.e. the function really throw. Then you could replace your 'jsonParsing(_:)' with a mock one just to convince yourself that the Swift 'do {} catch {}' does work. There could be something else in your code that might cause the unexpected behavior.

enum SimpleError: Error {
case userError(msg: String)
case systemError(msg: String)
}

func alwaysThrow() throws -> () {
throw SimpleError.userError(msg: "I create error!")
}


do {
print("before throw") // printed
try alwaysThrow()
print("after throw") // not printed
} catch {
print(error) // print: "userError("I create error!")\n"
}

(3) At this point, if it is not a user/programmer mistake, then focus your attention on the 'jsonParsing(_:)' as there is where the culprit is.

How to use try-catch in Swift?

In Swift there is no functionality to catch arbitrary runtime errors.

The developer is responsible to handle the errors properly.

For example

func divideStuff(a : Int, b : Int) -> Int {
if b == 0 { return 0 }
return a / b
}

Swift 2.1 do-try-catch not catching error

The do - catch combination is fine. This issue is simply one that cannot be caught - and therefore never makes it to the catch block.

If the issue were catchable (defined and handled via Swift's throws functionality), the catch block would've been executed.


Some semantics: there is a long-standing argument about the differences between the terms error and exception.

Some developers consider the two terms to be distinct. In this case, the term error represents an issue that was designed to be handled. Swift's throws action would fit here. In this case, a do - catch combination would allow the issue to be caught.

An exception, for these developers, represents an unexpected, usually fatal, issue that cannot be caught and handled. (Generally, even if you could catch it, you would not be able to handle it.)

Others consider the two terms to be equivalent and interchangeable, regardless of whether the issue in question can be caught or not. (Apple's documentation seems to follow this philosophy.)

(Updated to focus on the answer rather than the semantics.)

Try-Catch statement in Swift completing Try statement but printing Catch error

Update your code to print the details of the error instead.

do {
// Update our normalized local store immediately for a responsive UI
try transaction?.update(query: PostsQuery()) { (data: inout PostsQuery.Data) in
data.allPosts?.append(PostsQuery.Data.AllPost.init(id: uniqueId, title: mutation.title, author: mutation.author, content: mutation.content, version: 0))
}
} catch let error {
print(error)
}

The catch block will only be called when an error is thrown inside the do block, so you need to see what the error is and handle that case

How to handle errors/exceptions in swift

The Swift do-try-catch syntax is designed to handle the errors analogous to the NSError patterns in Objective-C, not for handling fatal errors/exceptions. Do not be confused by the similarity of Swift's error handling do-try-catch with the completely different Objective-C exception handling pattern of @try-@catch.

One should only use forced unwrapping (!) and forced type casting (as!) when one knows with certainty that they cannot possibly fail. If they could fail (as in this case), you should gracefully detect this scenario and handle it accordingly.

You could, for example, use Swift error handling to communicate a failure converting a string to a date (and then use do-try-catch pattern when you call it in order to detect and handle that error):

enum DateError: Error {
case badDate
case dateNotFound
}

func getDateFromTextfield() throws -> Date {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

guard let dateString = dictOfLabelsAndText["Birthdate"] as? String else {
throw DateError.dateNotFound
}

guard let date = formatter.date(from: dateString) else {
throw DateError.badDate
}

return date
}

Then you could do:

do {
child.birthdateAT = try getDateFromTextfield()
} catch {
child.birthdateAT = formatter.date(from: "01.01.2000")
}

Or, more concisely, use try? and nil coalescing operator:

child.birthdateAT = try? getDateFromTextfield() ?? formatter.date(from: "01.01.2000")

Alternatively, you might just change the method to return an optional and use nil as a way of detecting a failure:


func getDateFromTextfield() -> Date? {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

guard let dateString = dictOfLabelsAndText["Birthdate"] as? String else {
return nil
}

return formatter.date(from: dateString)
}

And then do:

if let birthDate = getDateFromTextfield() {
child.birthdateAT = birthDate
} else {
child.birthdateAT = formatter.date(from: "01.01.2000")
}

Or, again, use the nil coalescing operator:

child.birthdateAT = getDateFromTextfield() ?? formatter.date(from: "01.01.2000")

But, bottom line, do not use ! to unwrap an optional unless you know it can never be nil. Otherwise, use optional binding and/or guard statements to gracefully detect and report the failure.


This has been updated for contemporary versions of Swift. For original Swift 2 answer, see previous revision of this answer.

Swift: if a do return try fails does catch execute

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.

Nested do catch swift 3.0

If the actual errors thrown from those function calls are not needed
then you can use try? to convert the result to an optional,
and chain the calls with the nil-coalescing operator ??.

For example:

if let result = (try? firstThing()) ?? (try? secondThing()) ?? (try? thirdThing()) {
return result
} else {
// everything failed ...
}

Or, if the error from the last method should be thrown if everything fails,
use try? for all but the last method call:

return (try? firstThing()) ?? (try? secondThing()) ?? (try thirdThing())


Related Topics



Leave a reply



Submit