web-dev-qa-db-fra.com

Générez votre propre code d'erreur dans Swift 3

Ce que j'essaie de faire est d'exécuter une requête URLSession dans Swift 3. J'effectue cette action dans une fonction distincte (afin de ne pas écrire le code séparément pour GET et POST), de retourner la URLSessionDataTask et de gérer le succès et l'échec des fermetures . Un peu comme ça-

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

Je ne souhaite pas gérer la condition d'erreur dans cette fonction et souhaite générer une erreur à l'aide du code de réponse et renvoyer cette erreur pour qu'elle soit traitée quelle que soit l'origine de cette fonction. Quelqu'un peut-il me dire comment s'y prendre? Ou n'est-ce pas le moyen "rapide" de gérer de telles situations?

51
Rikh

Dans votre cas, l'erreur est que vous essayez de générer une instance Error. Error dans Swift 3 est un protocole qui peut être utilisé pour définir une erreur personnalisée. Cette fonctionnalité est spécialement conçue pour les applications Swift pures s'exécutant sous différents systèmes d'exploitation 

Dans le développement iOS, la classe NSError est toujours disponible et conforme au protocole Error.

Donc, si votre but est seulement de propager ce code d'erreur, vous pouvez facilement remplacer 

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

avec

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

Sinon, vérifiez le réponse de Sandeep Bhandari concernant la création d'un type d'erreur personnalisé.

73
Luca D'Alberti

Vous pouvez créer un protocole conforme au protocole Swift LocalizedError avec les valeurs suivantes:

protocol OurErrorProtocol: LocalizedError {

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

Cela nous permet ensuite de créer des erreurs concrètes comme ceci:

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
    }
}
49
Harry Bloom

Vous pouvez créer des enums pour traiter les erreurs :)

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

puis créez une méthode dans enum pour recevoir le code de réponse http et renvoyer l'erreur correspondante en retour :)

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

Enfin, mettez à jour votre bloc d’échec pour accepter un seul paramètre de type RikhError :)

J'ai un tutoriel détaillé sur la façon de restructurer le modèle de réseau orienté objet basé sur Objective - C basé sur le modèle moderne orienté protocole utilisant Swift3 ici https://learnwithmehere.blogspot.in Regardez :)

J'espère que ça aide :)

35
Sandeep Bhandari

Implémentez 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
    }
}

Notez que la simple implémentation de Error, par exemple, comme décrit dans l’une des réponses, échouera (du moins dans Swift 3) et que l’appel de localizedDescription entraînera la chaîne "L’opération n’a pas pu aboutir. (Erreur 1.StringError 1.) "

13
prewett

Détails

  • Xcode version 10.2.1 (10E1001)
  • Swift 5

Solution d'erreurs d'organisation dans une application

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

Usage

//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)
}
5
Vasily Bodnarchuk

Je sais que vous êtes déjà satisfait d'une réponse, mais si vous êtes intéressé à connaître la bonne approche, cela pourrait vous être utile ... Je préférerais ne pas mélanger le code d'erreur http-response avec le code d'erreur dans l'objet error (confus? S'il vous plaît continuer à lire un peu ...).

Les codes de réponse http sont des codes d'erreur standard relatifs à une réponse http définissant des situations génériques lors de la réception d'une réponse. Ils varient de 1xx à 5xx (par exemple, 200 OK, 408 demandes expirées, 504 Timeout de la passerelle, etc. - http: //www.restapitutorial .com/httpstatuscodes.html )

Le code d'erreur dans un objet NSError fournit une identification très spécifique du type d'erreur décrit par l'objet pour un domaine particulier d'application/produit/logiciel. Par exemple, votre application peut utiliser 1000 pour "Désolé, vous ne pouvez pas mettre à jour cet enregistrement plus d'une fois par jour" ou 1001 pour "Vous avez besoin du rôle de responsable pour accéder à cette ressource" ... spécifiques à votre domaine/application. logique.

Pour une très petite application, ces deux concepts sont parfois fusionnés. Mais comme vous pouvez le constater, ils sont complètement différents et très importants et utiles pour concevoir et utiliser de gros logiciels.

Donc, il peut y avoir deux techniques pour mieux gérer le code:

1. Le rappel de fin d'exécution effectuera toutes les vérifications

completionHandler(data, httpResponse, responseError) 

2. Votre méthode décide du succès et de la situation d'erreur, puis appelle le rappel correspondant.

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

Bonne codage :)

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

créer un objet NSError et le transtyper en Error, le montrer n'importe où

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

    var localizedTitle: String
    var localizedDescription: String

}

enum RequestError : Int, Error {

    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)
}
0
Daniel.scheibe