Handle error in Swift 4.2 without downcasting error - swift

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

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)
}

How to fix "Invalid conversion from throwing function of type X to non-throwing function type Y" with Do-Try-Catch

I am getting the error: Invalid conversion from throwing function of type '(_) throws -> ()' to non-throwing function type '(Either) -> Void'
on the line after do {, weatherApi.weather(with: weatherEndpoint) { (either) in
class WeatherTableViewController: UITableViewController {
var cellViewModels = [WeatherCellViewModel]()
override func viewDidLoad() {
super.viewDidLoad()
let weatherApi = WeatherAPIClient()
let weatherEndpoint = WeatherEndpoint.fiveDayForecast(city: "Atlanta", country: "us")
do {
weatherApi.weather(with: weatherEndpoint) { (either) in
switch either {
case .value(let Weather):
print("Made it")
print(Weather)
let data = try Weather.map {
$0.weather.map {
WeatherCellViewModel(description: $0.description)
}
}
print("data")
print(data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .error(let error):
print(error)
}
}
} catch {
print("weather endpoint error")
}
}
I added the do try catch after getting the same error originally and looking at the answers to similar problems, but it did not solve it. The try block is the critical part causing the error, where I am parsing my data for the description.
The do-catch is currently outside of the closure that has the try statement in it.
You'll need to move the do-catch into the closure. The error is because weather is expecting a closure that doesn't throw but it's getting on that does.
You wanted to throw from within the closure, which is not possible. so you need to place do-catch into the closure
Try this way:
do {
let data = try Weather.map {
$0.weather.map {
WeatherCellViewModel(description: $0.description)
}
}
} catch let error as NSError {
print(error)
}

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")
}

Swift try-catch handling within a function or class

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.

Catch multiple errorTypes?

I'm looking for a way to catch multiple types of errors in a catch. I've tried fallthrough and the comma separated style from a switch statement and neither works. The docs say nothing about catching multiple but pattern 1. It's not clear to me which of the pattern syntaxes would work here.
Error definitions (sample):
enum AppErrors {
case NotFound(objectType: String, id: Int)
case AlreadyUsed
}
Works:
do {
//...
} catch AppErrors.NotFound {
makeNewOne()
} catch AppErrors.AlreadyUsed {
makeNewOne()
} catch {
print("Unhandled error: \(error)")
}
Does not compile, is it possible to do something like this?
do {
//...
} catch AppErrors.NotFound, AppErrors.AlreadyUsed {
makeNewOne()
} catch {
print("Unhandled error: \(error)")
}
If you want to catch all AppErrors, you can use this pattern:
catch is AppErrors
If you're looking for more specific matching, it seems to quickly get ugly.
This will let us catch specific cases of AppErrors:
catch let error as AppErrors where error == .NotFound || error == .AlreadyUsed
There's also this syntax which seems to work:
catch let error as AppErrors where [.NotFound, .AlreadyUsed].contains(error)
For completeness sake, I'll also add this option, which allows us to catch errors of two different types, but it doesn't allow us to specify which case within those types:
catch let error where error is AppErrors || error is NSError
Finally, based on the fact that anything we catch will conform to the ErrorType protocol, we can clean up the second & third examples I provided with an ErrorType extension and use that in conjunction where a where clause in our catch:
extension ErrorType {
var isFooError: Bool {
guard let err = self as? AppErrors else { return false }
return err == .NotFound || err == .AlreadyUsed
}
}
And just catch it like this:
catch let error where error.isFooError
You can create a case that contains an AppErrors array:
indirect enum AppErrors: Error {
case NotFound
case AlreadyUsed
case errors([AppErrors])
}
Then, for the catch statement:
catch AppErrors.errors(let errors) where errors == [.NotFound, .AlreadyUsed]
Do note, however, that errors is an Array and the order matters when comparing with ==. An alternative is to use case errors(Set<AppErrors>), but that would require AppErrors to conform to Hashable protocol.
UPDATE: Come to think of it, it's better to use an OptionSet type:
public struct InvalidInput: OptionSet, Error {
public var rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
public static let noAccount = InvalidInput(rawValue: 1 << 0)
public static let noKey = InvalidInput(rawValue: 1 << 1)
public static let invalidKey = InvalidInput(rawValue: 1 << 2)
}
func validateInput() throws -> Void {
var invalid: InvalidInput = []
invalid.insert(.noKey)
invalid.insert(.noAccount)
if !invalid.isEmpty {
throw invalid
}
}
do {
try validateInput()
} catch [InvalidInput.noAccount, InvalidInput.noKey] as InvalidInput {
print("Account and key are both required.")
}
Link: http://www.chrisamanse.xyz/2016/12/03/catching-multiple-errors-in-swift
Multi-Pattern Catch Clauses proposal (SE-0276) is implemented in Swift 5.3
And now code snippet from the question is valid:
do {
//...
} catch AppErrors.NotFound, AppErrors.AlreadyUsed {
makeNewOne()
} catch {
print("Unhandled error: \(error)")
}
You can check updated docs.