Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 387 Vote(s) - 3.39 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Is there a better way to compare errors in Swift?

#1
I have this error:

public enum AuthenticationError: Error {
case unknownError
case canceledByUser
case userOrPasswordMismatch
case unableToExtractOneTimeCode
case unableToExchangeOneTimeCodeForToken
case credentialsUnavailable
case expired
case webRequestFailed(error: Error)
}

I'd like to overload `==` to compare it but I find I need to repeat the code three ways:

public func == (lhs: Error, rhs: AuthenticationError) -> Bool {
return lhs._code == rhs._code && lhs._domain == rhs._domain
}

public func == (lhs: AuthenticationError, rhs: Error) -> Bool
public func == (lhs: AuthenticationError, rhs: AuthenticationError) -> Bool

An usage example is:

if let error = error, error == AuthenticationError.expired {
// do something
}

Is there a better way to do this (with generics) so I don't need to repeat similar code?


Reply

#2
**Swift 5**

You just need to inherit `Equatable` protocol for your enum to make it comparable e.g.:

```
enum MyError: Error, Equatable {
case unknown
case canceled
case expired
}

let err1 = MyError.unknown
let err2 = MyError.canceled
let err3 = MyError.canceled
print("err1 == err2 - \(err1 == err2)")
print("err2 == err3 - \(err2 == err3)")


Prints:

err1 == err2 - false
err2 == err3 - true
```

But in your case you have `Error` in associated value so that you get next error:
```
public enum AuthenticationError: Error, Equatable {
...
}
// error: type 'AuthenticationError' does not conform to protocol 'Equatable'
// note: associated value type 'Error' does not conform to protocol 'Equatable', preventing synthesized conformance of 'AuthenticationError' to 'Equatable'
```

Thats because `Error` protocol do not conform to `Equatable` and we can't extend one protocol with the other:

```
extension Error: Equatable {
}
// error: Extension of `protocol` cant have inheritance clause
```

To fix this let's create a error wrapper that conforms to `Equatable` and compares your errors by reflecting strings:

```
public struct ErrorWrapper: Equatable {
let error: Error

public static func == (lhs: Self, rhs: Self) -> Bool {
String(reflecting: lhs.error) == String(reflecting: rhs.error)
}
}

public enum AuthenticationError: Error, Equatable {
case unknownError
case canceledByUser
case userOrPasswordMismatch
case unableToExtractOneTimeCode
case unableToExchangeOneTimeCodeForToken
case credentialsUnavailable
case expired
case webRequestFailed(ErrorWrapper)
}

let err1 = AuthenticationError.unknownError
let err2 = AuthenticationError.webRequestFailed(ErrorWrapper(error: error))
let err3 = AuthenticationError.webRequestFailed(ErrorWrapper(error: error))
print("err1 == err2 - \(err1 == err2)")
print("err2 == err3 - \(err2 == err3)")

Prints:

err1 == err2 - false
err2 == err3 - true
```
Reply

#3
I got an error, "Enum case ... is not a member of type ...", with the if-case in Xcode v12.1 and Swift 5. It seems the best to use switch-case statement. It also can prevent an unhandled case.

public enum AuthenticationError: Error {
case unknownError
case canceledByUser
case userOrPasswordMismatch
case unableToExtractOneTimeCode
case unableToExchangeOneTimeCodeForToken
case credentialsUnavailable
case expired
case webRequestFailed(error: Error)
}

switch error {
case .expired:
print("It is expired.")
default:
print("other error: \(error)")
}
Reply

#4
## Details ##

- Swift 5.1
- Xcode 11.6 (11E708)

## Solution ##

public func == (lhs: Error, rhs: Error) -> Bool {
guard type(of: lhs) == type(of: rhs) else { return false }
let error1 = lhs as NSError
let error2 = rhs as NSError
return error1.domain == error2.domain && error1.code == error2.code && "\(lhs)" == "\(rhs)"
}

extension Equatable where Self : Error {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs as Error == rhs as Error
}
}

## Usage ##

enum MyError: Error { case problem, bigProblem, catastrophicException }
enum MyError2: Error, Equatable { case oops, RUUUUUN(where: String) }

var errors = [Error]()
errors.append(MyError.problem)
errors.append(MyError.catastrophicException)
errors.append(MyError2.oops)
errors.append(MyError2.RUUUUUN(where: "I don't know"))
errors.append(MyError2.RUUUUUN(where: "No matter!!!"))
errors.append(NSError(domain: "domain", code: 234, userInfo: nil))
errors.append(NSError(domain: "domain2", code: 345, userInfo: nil))

for i in 0..<errors.count {
for j in i..<errors.count {
print("==============================================")
print("Error1: \(errors[i])")
print("Error2: \(errors[j])")
let result = errors[i] == errors[j]
print("(Error1 == Error2) = \(result ? "✅" : "❌" ) ")
}
}

## Console ##

==============================================
Error1: problem
Error2: problem
(Error1 == Error2) = ✅
==============================================
Error1: problem
Error2: catastrophicException
(Error1 == Error2) = ❌
==============================================
Error1: problem
Error2: oops
(Error1 == Error2) = ❌
==============================================
Error1: problem
Error2: RUUUUUN(where: "I don\'t know")
(Error1 == Error2) = ❌
==============================================
Error1: problem
Error2: RUUUUUN(where: "No matter!!!")
(Error1 == Error2) = ❌
==============================================
Error1: problem
Error2: Error Domain=domain Code=234 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: problem
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: catastrophicException
Error2: catastrophicException
(Error1 == Error2) = ✅
==============================================
Error1: catastrophicException
Error2: oops
(Error1 == Error2) = ❌
==============================================
Error1: catastrophicException
Error2: RUUUUUN(where: "I don\'t know")
(Error1 == Error2) = ❌
==============================================
Error1: catastrophicException
Error2: RUUUUUN(where: "No matter!!!")
(Error1 == Error2) = ❌
==============================================
Error1: catastrophicException
Error2: Error Domain=domain Code=234 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: catastrophicException
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: oops
Error2: oops
(Error1 == Error2) = ✅
==============================================
Error1: oops
Error2: RUUUUUN(where: "I don\'t know")
(Error1 == Error2) = ❌
==============================================
Error1: oops
Error2: RUUUUUN(where: "No matter!!!")
(Error1 == Error2) = ❌
==============================================
Error1: oops
Error2: Error Domain=domain Code=234 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: oops
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: RUUUUUN(where: "I don\'t know")
Error2: RUUUUUN(where: "I don\'t know")
(Error1 == Error2) = ✅
==============================================
Error1: RUUUUUN(where: "I don\'t know")
Error2: RUUUUUN(where: "No matter!!!")
(Error1 == Error2) = ❌
==============================================
Error1: RUUUUUN(where: "I don\'t know")
Error2: Error Domain=domain Code=234 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: RUUUUUN(where: "I don\'t know")
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: RUUUUUN(where: "No matter!!!")
Error2: RUUUUUN(where: "No matter!!!")
(Error1 == Error2) = ✅
==============================================
Error1: RUUUUUN(where: "No matter!!!")
Error2: Error Domain=domain Code=234 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: RUUUUUN(where: "No matter!!!")
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: Error Domain=domain Code=234 "(null)"
Error2: Error Domain=domain Code=234 "(null)"
(Error1 == Error2) = ✅
==============================================
Error1: Error Domain=domain Code=234 "(null)"
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ❌
==============================================
Error1: Error Domain=domain2 Code=345 "(null)"
Error2: Error Domain=domain2 Code=345 "(null)"
(Error1 == Error2) = ✅

Reply

#5
This suffices for your `.expired` check, without needing to define `==`:

let error: Error = ...
if case AuthenticationError.expired = error {
print("it's expired")
}

If you want to extract associated data (as in the `.webRequestFailed` case), you can do this:

if case AuthenticationError.webRequestFailed(error: let innerError) = error {
print("web request failed due to \(innerError.localizedDescription)")
}

Here's my macOS playground, created in Xcode 9.2:

import Foundation

public enum AuthenticationError: Error {
case unknownError
case canceledByUser
case userOrPasswordMismatch
case unableToExtractOneTimeCode
case unableToExchangeOneTimeCodeForToken
case credentialsUnavailable
case expired
case webRequestFailed(error: Error)
}

func test(_ error: Error) {
if case AuthenticationError.expired = error {
print("it's expired; error = \(error)")
} else if case AuthenticationError.webRequestFailed(error: let innerError) = error {
print("web request failed due to \(innerError.localizedDescription); error = \(error)")
} else {
print("no match; error = \(error)")
}
}

test(AuthenticationError.expired)
test(AuthenticationError.webRequestFailed(error: AuthenticationError.credentialsUnavailable))
test(NSError(domain: NSPOSIXErrorDomain, code: Int(ENOENT), userInfo: [:]))

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through