How can I catch and log all errors? - swift

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.

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 get the string I put into a Error instance?

Basicly I got this,
// MyPackage.swift
enum Error: LocalizedError {
case general(String)
}
func foobar() throws {
throw Error.general("haha")
}
do {
try foobar()
} catch Error.general(let message) {
// will print "haha"
print(message)
}
And then in the unit test, I need to check if I got the exact same error,
import Quick
import Nimble
import MyPackage
class MySpec: QuickSpec {
override func spec() {
describe("") {
let input = "haha"
let count = 2
let expectation = Error.general("非纯数字字符串无法使用本方法")
context("输入_" + input) {
it("预期_" + expectation.localizedDescription) {
// got the error
// but expectation.localizedDescription was not what I looking for
expect(try numberStringByAddingZerosInto(input, tillReach: count))
.to(throwError(expectation))
}
}
}
}
}
It worked, but expectation.localizedDescription was not "haha", which made the name of the test case useless.
I also tried expectation.errorDescription with no luck.
Where could I get it?
And why is it like this?
override errorDescription var
enum Error: LocalizedError {
case general(String)
var errorDescription: String? {
switch self {
case .general(let errorMessage):
return errorMessage
}
}
}
Now, you can also write do-catch block by this
do {
try foobar()
} catch let error {
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")
}

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.