有没有更好的方法来比较Swift中的错误?

时间:2018-04-04 19:25:39

标签: ios swift

我有这个错误:

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

我想重载==来比较它,但我发现我需要以三种方式重复代码:

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

一个用法示例是:

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

有没有更好的方法(使用泛型),所以我不需要重复类似的代码?

4 个答案:

答案 0 :(得分:8)

这足以进行.expired检查,无需定义==

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

如果要提取关联数据(如.webRequestFailed情况),您可以这样做:

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

这是我的macOS游乐场,在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: [:]))

答案 1 :(得分:5)

详细信息

  • Swift 5.1
  • Xcode 11.6(11E708)

解决方案

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

用法

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 ? "✅" : "❌" ) ")
    }
}

控制台

==============================================
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) = ✅ 

答案 2 :(得分:0)

我收到一个错误,“Enum case ... is not a member of type ...”,在 Xcode v12.1 和 Swift 5 中使用 if-case。似乎最好使用 switch-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)")
}

答案 3 :(得分:0)

Swift 5

您只需要为您的枚举继承 Equatable 协议以使其具有可比性,例如:

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

但在您的情况下,您的关联值中有 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'

那是因为 Error 协议不符合 Equatable 并且我们不能用另一个协议扩展一个协议:

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

为了解决这个问题,让我们创建一个符合 Equatable 的错误包装器,并通过反映字符串来比较您的错误:

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