快速生成自己的错误代码3


88

我想要实现的是URLSession在swift 3中执行一个请求。我正在一个单独的函数中执行此操作(以免为GET和POST分别编写代码),并返回URLSessionDataTask和处理闭包的成功和失败。有点像这样-

let task = URLSession.shared.dataTask(with: request) { (data, uRLResponse, responseError) in

     DispatchQueue.main.async {

          var httpResponse = uRLResponse as! HTTPURLResponse

          if responseError != nil && httpResponse.statusCode == 200{

               successHandler(data!)

          }else{

               if(responseError == nil){
                     //Trying to achieve something like below 2 lines
                     //Following line throws an error soo its not possible
                     //var errorTemp = Error(domain:"", code:httpResponse.statusCode, userInfo:nil)

                     //failureHandler(errorTemp)

               }else{

                     failureHandler(responseError!)
               }
          }
     }
}

我不希望在此函数中处理错误情况,并且希望使用响应代码生成错误并返回此Error以处理从何处调用此函数。谁能告诉我该怎么做?还是这不是处理此类情况的“快速”方法?


尝试在声明()中使用NSError而不是Errorvar errorTemp = NSError(...)
卢卡·阿尔贝蒂

这就解决了问题,但我认为Swift 3不想继续使用NS吗?
Rikh

它确实在iOS开发中。对于纯Swift开发,您应该通过遵循Error协议来创建自己的错误实例
Luca D'Alberti

@ LucaD'Alberti好,您的解决方案确实解决了问题,请随时将其添加为答案,以便我接受!
Rikh

Answers:


72

您可以LocalizedError使用以下值创建符合Swift协议的协议:

protocol OurErrorProtocol: LocalizedError {

    var title: String? { get }
    var code: Int { get }
}

然后,这使我们能够创建如下具体错误:

struct CustomError: OurErrorProtocol {

    var title: String?
    var code: Int
    var errorDescription: String? { return _description }
    var failureReason: String? { return _description }

    private var _description: String

    init(title: String?, description: String, code: Int) {
        self.title = title ?? "Error"
        self._description = description
        self.code = code
    }
}

3
a)不必创建OurErrorProtocol,只需让CustomError直接实现Error。b)这不起作用(至少在Swift 3中:从未调用过localizedDescription,并且您收到“操作无法完成。”)。您需要改为实现LocalizedError;看我的答案。
prewett

@prewett我刚刚注意到,但是你是对的!实际上,在LocalizedError中实现errorDescription字段会设置消息,而不是使用如上所述的my方法。我仍然保留“ OurErrorProtocol”包装,因为我也需要localizedTitle字段。感谢您指出了这一点!
哈里·布鲁姆

106

在您的情况下,错误是您正在尝试生成Error实例。ErrorSwift 3中的协议是可用于定义自定义错误的协议。此功能特别适合纯Swift应用程序在不同的OS上运行。

在iOS开发中,NSError该类仍然可用,并且符合Error协议。

因此,如果您的目的只是传播此错误代码,则可以轻松地替换

var errorTemp = Error(domain:"", code:httpResponse.statusCode, userInfo:nil)

var errorTemp = NSError(domain:"", code:httpResponse.statusCode, userInfo:nil)

否则,请检查有关如何创建自定义错误类型的Sandeep Bhandari答案


15
我刚收到错误:Error cannot be created because it has no accessible initializers
Supertecnoboff

@AbhishekThapliyal您能否再详细说明一下?我不明白你的意思。
卡·达伯蒂

2
@ LucaD'Alberti,如Swift 4中那样,由于创建错误对象时没有可访问的初始化程序,因此无法创建显示错误的错误。
Maheep '18

1
@Maheep我在回答中建议的不是使用Error,而是NSError。当然使用Error会引发错误。
卡·达伯蒂

错误是协议。不能直接实例化。
斯洛博丹

52

您可以创建枚举来处理错误:)

enum RikhError: Error {
    case unknownError
    case connectionError
    case invalidCredentials
    case invalidRequest
    case notFound
    case invalidResponse
    case serverError
    case serverUnavailable
    case timeOut
    case unsuppotedURL
 }

然后在enum中创建一个方法来接收http响应代码,并在return中返回相应的错误:)

static func checkErrorCode(_ errorCode: Int) -> RikhError {
        switch errorCode {
        case 400:
            return .invalidRequest
        case 401:
            return .invalidCredentials
        case 404:
            return .notFound
        //bla bla bla
        default:
            return .unknownError
        }
    }

最后,更新您的故障块以接受RikhError类型的单个参数:)

我有一个详细的教程,介绍如何使用Swift3将传统的基于Objective-C的面向对象的网络模型重构为现代的面向协议的模型,这里https://learnwithmehere.blogspot.in看看:)

希望能帮助到你 :)


啊,但这不是必须让我手动处理所有案件吗?那是输入错误代码吗?
Rikh

是的,您必须:D但是同时您可以针对每种错误状态采取各种措施:)现在,您可以对错误模型进行精细控制,如果万一您不想这样做,可以使用case 400 ... 404 {...}只处理一般情况:)
Sandeep Bhandari

是啊 谢谢
Rikh

假设多个http代码不需要指向相同的情况,则应该只可以枚举RikhError:Int,Error {case invalidRequest = 400},然后创建它RikhError(rawValue:httpCode)
Brian F Leighty

48

您应该使用NSError对象。

let error = NSError(domain:"", code:401, userInfo:[ NSLocalizedDescriptionKey: "Invalid access token"])

然后将NSError转换为Error对象


27

细节

  • Xcode版本10.2.1(10E1001)
  • 迅捷5

解决应用中的错误的解决方案

import Foundation

enum AppError {
    case network(type: Enums.NetworkError)
    case file(type: Enums.FileError)
    case custom(errorDescription: String?)

    class Enums { }
}

extension AppError: LocalizedError {
    var errorDescription: String? {
        switch self {
            case .network(let type): return type.localizedDescription
            case .file(let type): return type.localizedDescription
            case .custom(let errorDescription): return errorDescription
        }
    }
}

// MARK: - Network Errors

extension AppError.Enums {
    enum NetworkError {
        case parsing
        case notFound
        case custom(errorCode: Int?, errorDescription: String?)
    }
}

extension AppError.Enums.NetworkError: LocalizedError {
    var errorDescription: String? {
        switch self {
            case .parsing: return "Parsing error"
            case .notFound: return "URL Not Found"
            case .custom(_, let errorDescription): return errorDescription
        }
    }

    var errorCode: Int? {
        switch self {
            case .parsing: return nil
            case .notFound: return 404
            case .custom(let errorCode, _): return errorCode
        }
    }
}

// MARK: - FIle Errors

extension AppError.Enums {
    enum FileError {
        case read(path: String)
        case write(path: String, value: Any)
        case custom(errorDescription: String?)
    }
}

extension AppError.Enums.FileError: LocalizedError {
    var errorDescription: String? {
        switch self {
            case .read(let path): return "Could not read file from \"\(path)\""
            case .write(let path, let value): return "Could not write value \"\(value)\" file from \"\(path)\""
            case .custom(let errorDescription): return errorDescription
        }
    }
}

用法

//let err: Error = NSError(domain:"", code: 401, userInfo: [NSLocalizedDescriptionKey: "Invaild UserName or Password"])
let err: Error = AppError.network(type: .custom(errorCode: 400, errorDescription: "Bad request"))

switch err {
    case is AppError:
        switch err as! AppError {
        case .network(let type): print("Network ERROR: code \(type.errorCode), description: \(type.localizedDescription)")
        case .file(let type):
            switch type {
                case .read: print("FILE Reading ERROR")
                case .write: print("FILE Writing ERROR")
                case .custom: print("FILE ERROR")
            }
        case .custom: print("Custom ERROR")
    }
    default: print(err)
}

16

实现LocalizedError:

struct StringError : LocalizedError
{
    var errorDescription: String? { return mMsg }
    var failureReason: String? { return mMsg }
    var recoverySuggestion: String? { return "" }
    var helpAnchor: String? { return "" }

    private var mMsg : String

    init(_ description: String)
    {
        mMsg = description
    }
}

请注意,例如,如答案之一所述,仅实现Error会失败(至少在Swift 3中如此),并且调用localizedDescription会导致字符串“操作无法完成。(。StringError错误1。) ”


应该是mMsg = msg
Brett

1
糟糕,是的。我将“ msg”更改为“ description”,希望它比我的原始内容更清晰。
prewett

4
您可以将其减少为struct StringError : LocalizedError { public let errorDescription: String? },并将其简单地用作StringError(errorDescription: "some message")
Koen。

7
 let error = NSError(domain:"", code:401, userInfo:[ NSLocalizedDescriptionKey: "Invaild UserName or Password"]) as Error
            self.showLoginError(error)

创建一个NSError对象并将其类型转换为Error,在任何地方显示

private func showLoginError(_ error: Error?) {
    if let errorObj = error {
        UIAlertController.alert("Login Error", message: errorObj.localizedDescription).action("OK").presentOn(self)
    }
}

4

我仍然认为Harry的答案是最简单且完整的,但是如果您需要更简单的答案,请使用:

struct AppError {
    let message: String

    init(message: String) {
        self.message = message
    }
}

extension AppError: LocalizedError {
    var errorDescription: String? { return message }
//    var failureReason: String? { get }
//    var recoverySuggestion: String? { get }
//    var helpAnchor: String? { get }
}

并像这样使用或测试它:

printError(error: AppError(message: "My App Error!!!"))

func print(error: Error) {
    print("We have an ERROR: ", error.localizedDescription)
}

2
protocol CustomError : Error {

    var localizedTitle: String
    var localizedDescription: String

}

enum RequestError : Int, CustomError {

    case badRequest         = 400
    case loginFailed        = 401
    case userDisabled       = 403
    case notFound           = 404
    case methodNotAllowed   = 405
    case serverError        = 500
    case noConnection       = -1009
    case timeOutError       = -1001

}

func anything(errorCode: Int) -> CustomError? {

      return RequestError(rawValue: errorCode)
}

1

我知道您已经对答案感到满意,但是如果您想知道正确的方法,那么这可能对您有所帮助。我不希望在错误对象中将HTTP响应错误代码与错误代码混合使用(混淆?请继续阅读...)。

http响应代码是有关http响应的标准错误代码,定义了收到响应时的一般情况,范围从1xx到5xx(例如200 OK,408请求超时,504网关超时等-http: //www.restapitutorial.com/ httpstatuscodes.html

NSError对象中的错误代码非常明确地标识了对象针对应用程序/产品/软件的特定领域描述的错误类型。例如,您的应用程序可能对您的域/应用程序使用1000(表示“抱歉,您每天不能多次更新此记录”)或1001(表示“您需要管理员角色才能访问此资源”)...逻辑。

对于非常小的应用程序,有时会将这两个概念合并。但是,正如您所看到的,它们是完全不同的,对于设计和使用大型软件非常重要且很有帮助。

因此,有两种技术可以更好地处理代码:

1.完成回调将执行所有检查

completionHandler(data, httpResponse, responseError) 

2.您的方法确定成功和错误的情况,然后调用相应的回调

if nil == responseError { 
   successCallback(data)
} else {
   failureCallback(data, responseError) // failure can have data also for standard REST request/response APIs
}

快乐的编码:)


因此,从服务器返回的特定错误代码的情况下,如果要显示某些特定的字符串,那么基本上您想说的就是传递“ data”参数?(对不起,我有时会有点慢!)
Rikh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.