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
Swift 5.0: 'Withunsafebytes' Is Deprecated: Use 'Withunsafebytes≪R≫(...)
Accessing an Enumeration Association Value in Swift
How to Compare One Value Against Multiple Values - Swift
Custom Back Button For Navigationview'S Navigation Bar in Swiftui
Get Integer Value from String in Swift
Deleting a Row from a Uitableview in Swift
Firebase Getting Data in Order
How to Improve People Occlusion in Arkit 3.0
How to Return an Object That I Create in My Data Service Class Through Firebase
Append Text or Data to Text File in Swift
How to Compare Enum With Associated Values by Ignoring Its Associated Value in Swift
Differences in Nsdatecomponents Syntax
Xcode Swift Am/Pm Time to 24 Hour Format
Swift 2 - Pattern Matching in "If"
How to Detect a Tap Gesture Location in Swiftui