Swift try-catch handling within a function or class - swift

I try to become acquainted with the try&catch construct in Swift.
Sorry, i couldn't find an answer in related posts!
Is it possible to hide this into a function or class as shown here?
class Test {
enum TestError:ErrorType{
case a
case b
}
func a(s:String) throws -> String{
if s == "OK" {
return "OK"
}else{
throw TestError.a
}
}
func b(s:String) throws -> String{
if s == "OK" {
return "OK"
}else{
throw TestError.b
}
}
func doTest(str:String) -> String{
do{
let s = try a(b(str)) // <= ERROR
}catch TestError.a{
return "a failed!"
}catch TestError.b{
return "b failed!"
}
}
}
I always get
error: errors thrown from here are not handled because the enclosing
catch is not exhaustive
Is it in principle impossible and only applicable in the main program?
Or is there a way around?

The compiler needs a clause in case a different error is thrown. I like to do a global catch like this, by casting to NSError:
do {
return try a(b(str))
} catch TestError.a {
return "a failed!"
} catch TestError.b {
return "b failed!"
} catch let error as NSError {
return "\(error.description)"
}
You also have to return the String at the try.

Related

What error do I need to throw to for a specific catch block to handle + Swift error handling

I want to add code to throw an error that requires the "catch let printerErrorsecond" and the last catch block to handle an error. I have tried updating the value for the toPrinter parameter to a number of values without any luck. I also tried a number of else if statements and cannot seem to get either of the last two catch blocks to handle the errors. It seems like a case for both could be added in the PrinterError enum but after several attempts I could not resolve the issue. Any code based resolutions you can provide the better!
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
} else if printerName == "Fire" {
throw PrinterError.onFire
} else if printerName == "Empty" {
throw PrinterError.outOfPaper
}
return "Job sent"
}
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
// Prints "Job sent"
try to implement it by this way
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
case deFault
case noHandeling
}
func send(job: Int, toPrinter printerName: String) throws -> String {
switch printerName {
case PrinterError.outOfPaper.localizedDescription:
print("outOfPaper")
return PrinterError.outOfPaper.localizedDescription
case PrinterError.noToner.localizedDescription:
print("noToner")
return PrinterError.noToner.localizedDescription
case PrinterError.onFire.localizedDescription:
print("onFire")
return PrinterError.onFire.localizedDescription
default:
print("default")
return PrinterError.deFault.localizedDescription
}
}
do {
try send(job: 100, toPrinter: "AAA")
} catch {
print(error.localizedDescription)
}

Swift error handling with multiple catch and conditions based on the error's enum associated value

Is there a way to catch errors with some condition on the catch based on the value of the associated value of the error enum?
Example:
enum Errors : Error {
case error1(str: String?) // optional associated value
case error2
case error3
}
func f() throws {
throw Errors.error1(str: nil)
}
do {
try f()
}
catch Errors.error1(let str) {
if(str != nil) {
print(str!)
}
else {
//throw the same error to be caught in the last catch
}
}
catch {
print("all other errors")
}
Yes of course!
In a catch, you can do pattern matching with the error, just like in a switch statement. You can read more about this in the Swift guide under the section "Handling errors using Do-Catch". This means you can use where:
do {
try f()
}
catch Errors.error1(let str) where str != nil{
}
catch {
print("all other errors")
}

Handle error in Swift 4.2 without downcasting error

I defined an error type called "HelloError", after multiple calls, I have to do downcast using as! and the error in do-catch block.
enum HelloError: Error {
case A
case B
}
func hello() throws {
try helloAgain()
}
func helloAgain() throws {
throw HelloError.A
}
do {
try hello()
} catch {
switch error as! HelloError {
case .A:
print("A")
case .B:
print("B")
}
}
Is it possible to catch the error without downcasting the error type? I want to write code like this:
do {
try hello()
} catch {
switch error {
case HelloError.A:
print("A")
case HelloError.B:
print("B")
}
}
Thank you.
You can do the switch with multiple catch blocks
do {
try hello()
} catch HelloError.A {
print("A")
} catch HelloError.B {
print("B")
}
More detailed information is in Swift Language Guide: Error handling

Generate Error Sheet from Swift Exception

I'd like to generate an NSAlert-style error sheet from a Swift exception handler. In the past with Objective-C I could simply use [NSAlert alertWithError:] Now with Swift and it's own exception handling system in the mix, alerts feel rather clunky:
enum Problems: ErrorType {
case Bad
case Worse
}
func f() throws {
throw Problems.Worse
}
func g() {
let errorMessage: String
do {
try f()
}
catch Problems.Bad {
errorMessage = "This was bad"
}
catch Problems.Worse {
errorMessage = "This was even worse"
}
catch {
errorMessage = "This was unexpected"
}
guard errorMessage == nil else {
let alert = NSAlert()
alert.messageText = "Error"
alert.informativeText = errorMessage
alert.beginSheetModalForWindow(self, completionHandler: { [unowned self] (returnCode) -> Void in
if returnCode == NSAlertFirstButtonReturn {
// Handle the response
}
})
}
This feels kludgy and like it doesn't scale well if there are many error conditions that must be checked. Is there a better way?
Swift allows your ErrorType to have associated data. This is more or less an exploded version of userInfo from NSError. If you want convenience, you can write a function to convert a Problems enum to NSError:
enum Problems: ErrorType {
case Small
case Bad(message: String?)
case Worse(message: String?)
func toNSError() -> NSError {
var errorMessage = ""
switch self {
case .Small:
errorMessage = "Small error"
case let .Bad(msg):
errorMessage = msg ?? "Default bad message"
case let .Worse(msg):
errorMessage = msg ?? "Default worse message"
}
return NSError(domain: NSCocoaErrorDomain, code: 1, userInfo: [NSLocalizedDescriptionKey: errorMessage])
}
}
func f() throws {
throw Problems.Worse(message: nil)
}
do {
try f()
} catch let error as Problems {
let err = error.toNSError()
// now display NSAlert with err...
}
You can throw an error with message: nil to get the default message, or add your own custom message from the local conditions:
throw Problems.Bad(message: "I have a bad feeling about this")

How can I catch and log all errors?

I'm using some third-party functions, and I don't know what types of errors they can produce.
I want to catch and log all such errors, but ErrorType protocol does not have any members that could provide a meaningful message or error code. What can be done to log any type of errors properly?
do {
try someFunction()
} catch WellKnownError { // handle it }
} catch let err {
let message = ... // how can I get error type and some meaningful message from err?
NSLog(message)
}
I think it's not an option...
But you can try something like this...
//----------------------------------------
// Private API
//----------------------------------------
public enum CarError : ErrorType
{
case NeverTurnOn
case OutOfFuel
case Other(String)
}
public class Car
{
public func turnOn(newState:Int) throws -> Bool
{
if newState == 1
{
throw CarError.NeverTurnOn
}
else if case 2...5 = newState
{
throw CarError.OutOfFuel
}
else if newState == 10
{
throw CarError.Other("Something is gonna wrong")
}
else if newState == 11
{
throw CarError.Other(":(")
}
return false
}
}
Error Handling
func main()
{
let car = Car()
do {
try car.turnOn(4)
} catch CarError.NeverTurnOn {
print("Turn off")
} catch let err as NSError {
print(err.description)
} catch {
print("It's madness!!!")
}
}
main()
With this test... I had this result...
//Error Domain=trycatchtest.CarError Code=1 "The operation couldn’t be completed. (trycatchtest.CarError error 1.)"
//Program ended with exit code: 0
It's not the best way to solve your problem, but a new way to understand what is happening in background of this framework.
Now, asking a favor.
Please, let me now about the final result ;)
It's doesn't matter if is a good or bad result.