See this error:
enum MyError: ErrorType {
case Foo
case Bar
}
func couldThrow(string: String) throws {
if string == "foo" {
throw MyError.Foo
} else if string == "bar" {
throw MyError.Bar
}
}
func asdf() {
do {
//Error: Errors thrown from here are not handled
//because the enclosing catch is not exhaustive.
try couldThrow("foo")
} catch MyError.Foo {
print("foo")
} catch MyError.Bar {
print("bar")
}
}
Yet my catches cover all the possibilities. Why doesn't Swift "deeply" analyze all the possibilities and tell me nothing is wrong?
For instance, search for "catch VendingMachineError.InvalidSelection" here: https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508
You will see in there that Apple is doing it my way. Is their code wrong?
It's very hard for the compiler to know exactly which exceptions a piece of code could result in, because any exceptions which aren't handled at a deeper level are propagated up. While your situation is relatively simple it's generically a very difficult problem.
Note that nowhere does the code say which exceptions it can throw, only that it can throw something...
Key statement:
For example, the following code handles all three cases of the VendingMachineError enumeration, but all other errors have to be handled by its surrounding scope
So, in their example, though they don't show it, the container for that piece of code must also be capable of throwing. This is because it doesn't handle all possible exceptions.
For your case, asdf needs to be defined throws or it needs a catch all.
Although Wain's answer is correct, there is another way to eliminate the error: use try! to treat any unhandled errors as fatal runtime errors.
func asdf() {
try! {
do {
//Error: Errors thrown from here are not handled
//because the enclosing catch is not exhaustive.
try couldThrow("foo")
} catch MyError.Foo {
print("foo")
} catch MyError.Bar {
print("bar")
}
}()
}
Related
I'm migrating some code originally written in Swift2 to Swift4. I've completed the Swift3 intermediate upgrade and am hitting some errors in Xcode9 regarding some user defined enum uses outside their source file.
Consider the following partial project structure...
Project
--EnumDefs
----ExceptionTypes.swift
--UI
----UseExceptions.swift
ExceptionTypes.swift
enum MyError : Error {
case err1
case err2
}
...
UseExceptions.swift
...
do {
...
} catch MyError.err1(let e) {
print("\(e)")
} catch let e {
print("\(e)")
}
...
I've also tried the variant syntax I've seen online of
catch let e as MyError.err1 {
still no luck, I'm seeing the compiler error:
Enum element 'err1' is not a member type of 'MyError'
I'm tried making the MyError scope to be defined public which didn't work. I get a sense that I may be missing an import or something. I'm not sure if it matters but Autocomplete in Xcode from the UseExceptions.swift file does recognize when I begin typing MyError.
Is there anything special to using definitions between swift files across a sibling directory like shown above? Or is there something else wrong here with the way Swift 4 deals with exception handling?
You are missing an associatedValue, case err1(String)
func test() throws {
throw MyError.err1("error")
}
enum MyError : Error {
case err1(String)
case err2
}
do {
try test()
} catch MyError.err1(let e) {
print(e)
}
Looks like #zombie was right, it was a matter of old style Exception handling. I removed the substantive use of the print("\(e)") from the err1 handler and the compiler error went away.
I'm curious what the exception handling capabilities were like in Swift 2 that allowed the earlier syntax and exception object usage.
Catch clauses in Swift must be exhaustive. Does it mean I always need to use a wildcard or empty catch clauses whenever I want to avoid error propagation? Example:
enum Oops: Error {
case oh, ouch, meh
}
func troublemaker() {
do { throw Oops.meh }
catch Oops.oh {}
catch Oops.ouch {}
catch Oops.meh {}
// Error: Error is not handled because the enclosing catch is not exhaustive
}
Of course, it is fixed if I add throws to the function. Same goes for adding either catch {} or catch _ {}.
But is there any way to make exhaustive catch blocks other way? Like, perhaps defining the allowed type of the error to throw, so my enum Error would make it exhaustive?
If you simply don't like the multiple catch blocks, catch all errors at once and then switch error types
func troublemaker() {
do { throw Oops.meh }
catch let error{
switch error {
case Oops.meh:
print("It works!")
break
default:
print("Oops something else is wrong")
break
}
}
}
I was dealing with a class where I have implemented a do-try-catch clause.
Basically the problem is that the code after "try" got executed even though the try fails:
Do{
try jsonparshing(mydata)
code....
}catch{
alert("error")
}
Is it correct to locate the code after try?
I don't want that this code get executed if try fails.
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.
Running the following snippet in a playground / project:
class Piece {
enum ParseError: Error {
case unknown(string: String)
}
class func parse(string: String) throws {
throw ParseError.unknown(string: string)
}
}
class Board {
class func parse(string: String) {
do {
try Piece.parse(string: string)
} catch Piece.ParseError.unknown(let string) {
print(string)
}
}
}
Gives a Swift Compiler Error:
Errors thrown from here are not handled because the enclosing catch is not exhaustive
The following works fine:
enum ParseError: Error {
case unknown(string: String)
}
func parse(string: String) throws {
throw ParseError.unknown(string: string)
}
do {
try parse(string: "rook")
} catch ParseError.unknown(let string) {
print(string)
}
Running Xcode 8.3 / Swift 3.1
What's the reason for the error?
As said by the language guide (emphasis mine):
The catch clauses don’t have to handle every possible error that the code in its do clause can throw. If none of the catch clauses handle the error, the error propagates to the surrounding scope. However, the error must be handled by some surrounding scope [...]
Therefore, your example
class Board {
class func parse(string: String) {
do {
try Piece.parse(string: string)
} catch Piece.ParseError.unknown(let string) {
print(string)
}
}
}
is illegal – because combined, the do-catch block and the enclosing scope (the actual method itself) don't exhaustively handle every possible error that Piece.parse(string: string) can throw (remember that a throwing function can throw any error type that conforms to the Error protocol).
You would either want to add a "catch all" block to your do-catch in order to handle any other thrown error:
do {
try Piece.parse(string: string)
} catch Piece.ParseError.unknown(let string) {
print(string)
} catch {
// do error handling for any miscellaneous errors here.
print(error)
}
Or make parse(string:) a throwing method in order to propagate any uncaught errors back to the caller.
class func parse(string: String) throws {
// ...
}
The only reason why
enum ParseError: Error {
case unknown(string: String)
}
func parse(string: String) throws {
throw ParseError.unknown(string: string)
}
do {
try parse(string: "rook")
} catch ParseError.unknown(let string) {
print(string)
}
compiles at the top-level of a main.swift file is simply because that scope is special. It can catch any uncaught error, and upon doing so will invoke fatalError() with a description of that error.
I cannot find any official documentation for this, but if you look in the Standard Library's ErrorType.swift file, you'll see the following function:
/// Invoked by the compiler when code at top level throws an uncaught error.
#_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
And if we examine the IR emitted for a simplified version of the above code, sure enough we can see that the compiler inserts a call to swift_errorInMain upon an uncaught error being thrown.
With a playground, you get a similar behaviour in that the compiler allows uncaught errors at the top-level – although in the case of an error being thrown, the playground just seems to silently terminate without displaying a fatal error message.
It's difficult to investigate further due to the fact that Swift playgrounds run code in their own special environment, therefore meaning that the runtime behaviour can wildly differ from code compiled with swiftc. Really, you should never use a playground to test the actual behaviour of Swift code.
Take a look to this answer:
"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."
https://stackoverflow.com/a/30720807/6203030
In your case, this can be solved by adding this:
class Board {
class func parse(string: String) {
do {
try Piece.parse(string: string)
} catch Piece.ParseError.unknown(let string) {
print(string)
} catch let error {
print(error.localizedDescription)
}
}
}
You need to catch all errors.
I give it a try to understand new error handling thing in swift 2. Here is what I did: I first declared an error enum:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
And then I declared a method that throws an error (not an exception folks. It is an error.). Here is that method:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
The problem is from the calling side. Here is the code that calls this method:
let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]
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")
}
After the do line compiler says Errors thrown from here are not handled because the enclosing catch is not exhaustive. But in my opinion it is exhaustive because there is only two case in SandwichError enum.
For regular switch statements swift can understands it is exhaustive when every case handled.
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.
I suspect this just hasn’t been implemented properly yet. The Swift Programming Guide definitely seems to imply that the compiler can infer exhaustive matches 'like a switch statement'. It doesn’t make any mention of needing a general catch in order to be exhaustive.
You'll also notice that the error is on the try line, not the end of the block, i.e. at some point the compiler will be able to pinpoint which try statement in the block has unhandled exception types.
The documentation is a bit ambiguous though. I’ve skimmed through the ‘What’s new in Swift’ video and couldn’t find any clues; I’ll keep trying.
Update:
We’re now up to Beta 3 with no hint of ErrorType inference. I now believe if this was ever planned (and I still think it was at some point), the dynamic dispatch on protocol extensions probably killed it off.
Beta 4 Update:
Xcode 7b4 added doc comment support for Throws:, which “should be used to document what errors can be thrown and why”. I guess this at least provides some mechanism to communicate errors to API consumers. Who needs a type system when you have documentation!
Another update:
After spending some time hoping for automatic ErrorType inference, and working out what the limitations would be of that model, I’ve changed my mind - this is what I hope Apple implements instead. Essentially:
// allow us to do this:
func myFunction() throws -> Int
// or this:
func myFunction() throws CustomError -> Int
// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int
Yet Another Update
Apple’s error handling rationale is now available here. There have also been some interesting discussions on the swift-evolution mailing list. Essentially, John McCall is opposed to typed errors because he believes most libraries will end up including a generic error case anyway, and that typed errors are unlikely to add much to the code apart from boilerplate (he used the term 'aspirational bluff'). Chris Lattner said he’s open to typed errors in Swift 3 if it can work with the resilience model.
Swift is worry that your case statement is not covering all cases, to fix it you need to create a default case:
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 Default {
print("Another Error")
}
I was also disappointed by the lack of type a function can throw, but I get it now thanks to #rickster and I'll summarize it like this: let's say we could specify the type a function throws, we would have something like this:
enum MyError: ErrorType { case ErrorA, ErrorB }
func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }
do {
try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }
The problem is that even if we don't change anything in myFunctionThatThrows, if we just add an error case to MyError:
enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }
we are screwed because our do/try/catch is no longer exhaustive, as well as any other place where we called functions that throw MyError
enum NumberError: Error {
case NegativeNumber(number: Int)
case ZeroNumber
case OddNumber(number: Int)
}
extension NumberError: CustomStringConvertible {
var description: String {
switch self {
case .NegativeNumber(let number):
return "Negative number \(number) is Passed."
case .OddNumber(let number):
return "Odd number \(number) is Passed."
case .ZeroNumber:
return "Zero is Passed."
}
}
}
func validateEvenNumber(_ number: Int) throws ->Int {
if number == 0 {
throw NumberError.ZeroNumber
} else if number < 0 {
throw NumberError.NegativeNumber(number: number)
} else if number % 2 == 1 {
throw NumberError.OddNumber(number: number)
}
return number
}
Now Validate Number :
do {
let number = try validateEvenNumber(0)
print("Valid Even Number: \(number)")
} catch let error as NumberError {
print(error.description)
}
Error can be handle using switch case in catch
func checkAge(age:Int) throws {
guard !(age>0 && age < 18) else{
throw Adult.child
}
guard !(age >= 60) else{
throw Adult.old
}
guard (age>0) else{
throw Adult.notExist
}
}
do{
try checkAge(age:0)
}
catch let error {
switch error{
case Adult.child : print("child")
case Adult.old : print("old")
case Adult.notExist : print("not Exist")
default:
print("default")
}
}
enum Adult:Error {
case child
case old
case notExist
}
Create enum like this:
//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}
Create method like:
func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
throw spendingError.limit
}
return morningSpending + eveningSpending
}
Now check error is there or not and handle it:
do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}