swift3で独自のエラーコードを生成する


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

この関数でエラー状態を処理したくないので、応答コードを使用してエラーを生成し、このエラーを返して、この関数が呼び出された場所でエラーを処理したいと思います。誰かがこれについてどうやって行くのか教えてもらえますか?それとも、これはそのような状況に対処するための「迅速な」方法ではありませんか?


宣言のNSError代わりに使用してみてくださいErrorvar errorTemp = NSError(...)
Luca D'Alberti

それで問題は解決しましたが、swift 3はNSの使用を継続したくないと思いましたか?
Rikh 2016年

iOS開発で行います。純粋なSwift開発の場合は、Errorプロトコルに準拠して独自のエラーインスタンスを作成する必要があります
Luca D'Alberti

@ LucaD'Albertiさて、あなたの解決策は問題を解決しました、私がそれを受け入れることができるように、答えとしてそれを自由に追加してください!
Rikh 2016年

回答:


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 2017年

@prewett気づいたばかりですが、あなたは正しいです!LocalizedErrorにerrorDescriptionフィールドを実装すると、実際には、上記のように私のメソッドを使用するのではなく、メッセージが設定されます。ただし、localizedTitleフィールドも必要なので、「OurErrorProtocol」ラッパーを保持しています。それを指摘してくれてありがとう!
ハリーブルーム

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)

それ以外の場合は、カスタムエラータイプの作成方法に関するSandeepBhandari回答を確認してください


15
エラーが発生します:Error cannot be created because it has no accessible initializers
スーパー

@AbhishekThapliyalコメントをもう少し詳しく教えていただけますか?どういう意味かわかりません。
Luca D'Alberti

2
@ LucaD'Albertiは、Swift 4と同様に、エラーオブジェクトの作成中に、アクセス可能な初期化子がないため、表示されているエラーを作成できません。
Maheep 2018年

1
@Maheep私の答えで提案しているのは、使用しないことErrorですが、NSError。もちろん、使用Errorするとエラーが発生します。
Luca D'Alberti

エラーはプロトコルです。直接インスタンス化することはできません。
slobodans

52

エラーを処理するための列挙型を作成できます:)

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

次に、列挙型内にメソッドを作成してhttp応答コードを受け取り、対応するエラーを返します:)

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タイプの単一パラメーターを受け入れます:)

従来のObjective-Cベースのオブジェクト指向ネットワークモデルをSwift3を使用して最新のプロトコル指向モデルに再構築する方法に関する詳細なチュートリアルがありますhttps://learnwithmehere.blogspot.inご覧ください:)

それが役に立てば幸い :)


ああ、でもこれですべてのケースを手動で処理する必要はありませんか?それはエラーコードを入力しますか?
rikh 2016年

うん、あなたは:Dする必要がありますが、同時に、各エラーステータスに固有のさまざまなアクションを実行できます:)これで、エラーモデルを細かく制御できます。それを実行したくない場合は、ケース400 ... 404を使用できます。 {...}一般的なケースのみを処理します:)
Sandeep Bhandari 2016年

うん!ありがとう
Rikh 2016年

複数のhttpコードが同じケースを指す必要がないと仮定すると、列挙型RikhError:Int、Error {case invalidRequest = 400}を実行し、それを作成することができます。RikhError(rawValue:httpCode)
Brian F

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

たとえば、回答の1つで説明されているように、単にErrorを実装すると失敗し(少なくともSwift 3では)、localizedDescriptionを呼び出すと、「操作を完了できませんでした。(。StringErrorエラー1)」という文字列が生成されることに注意してください。 「」


それはmMsg = msgである必要があります
Brett

1
おっと、そうです。「msg」を「description」に変更しました。これは、元のメッセージよりも少し明確になっていることを願っています。
prewett 2017年

4
あなたはそれに減らすことができstruct StringError : LocalizedError { public let errorDescription: String? }、それは単に使用StringError(errorDescription: "some message")
公園。

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

ハリーの答えはまだ最も単純で完成していると思いますが、さらに単純なものが必要な場合は、次を使用してください。

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-responseエラーコードとエラーオブジェクトのエラーコードを混在させたくない(混乱していますか?少し読み続けてください...)。

http応答コードは、応答が受信されたときの一般的な状況を定義するhttp応答に関する標準エラーコードであり、1xxから5xxまで変化します(例:200 OK、408要求のタイムアウト、504ゲートウェイタイムアウトなど-http://www.restapitutorial.com/ httpstatuscodes.html

NSErrorオブジェクトのエラーコードは、アプリケーション/製品/ソフトウェアの特定のドメインについてオブジェクトが説明するエラーの種類を非常に具体的に識別します。たとえば、アプリケーションで「申し訳ありませんが、このレコードを1日に2回以上更新することはできません」に1000を使用したり、「このリソースにアクセスするにはマネージャーの役​​割が必要です」に1001を使用したりできます。これはドメイン/アプリケーションに固有です。論理。

非常に小さなアプリケーションの場合、これら2つの概念が統合されることがあります。しかし、ご覧のとおり、これらは完全に異なり、大規模なソフトウェアの設計と操作に非常に重要で役立ちます。

したがって、コードをより適切に処理するには、次の2つの手法があります。

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 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.