Hasilkan kode kesalahan Anda sendiri di swift 3

91

Apa yang saya coba capai adalah melakukan URLSessionpermintaan dengan cepat 3. Saya melakukan tindakan ini dalam fungsi terpisah (agar tidak menulis kode secara terpisah untuk GET dan POST) dan mengembalikan URLSessionDataTaskserta menangani keberhasilan dan kegagalan dalam penutupan. Semacam seperti ini-

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

Saya tidak ingin menangani kondisi kesalahan dalam fungsi ini dan ingin menghasilkan kesalahan menggunakan kode respons dan mengembalikan Kesalahan ini untuk menanganinya dari mana pun fungsi ini dipanggil. Adakah yang bisa memberi tahu saya bagaimana melakukan ini? Atau bukankah ini cara "Cepat" untuk menangani situasi seperti itu?

Rikh
sumber
Coba gunakan NSErroralih-alih Errordi deklarasi ( var errorTemp = NSError(...))
Luca D'Alberti
Itu menyelesaikan masalah tapi saya pikir swift 3 tidak ingin terus menggunakan NS?
Rikh
Itu dilakukan dalam pengembangan iOS. Untuk pengembangan Swift murni, Anda harus membuat contoh kesalahan Anda sendiri dengan menyesuaikan Errorprotokol
Luca D'Alberti
@ LucaD'Alberti Solusi Anda memang memecahkan masalah, jangan ragu untuk menambahkannya sebagai jawaban sehingga saya dapat menerimanya!
Rikh

Jawaban:

74

Anda dapat membuat protokol, sesuai dengan LocalizedErrorprotokol Swift , dengan nilai-nilai berikut:

protocol OurErrorProtocol: LocalizedError {

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

Ini kemudian memungkinkan kita untuk membuat kesalahan konkret seperti:

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
    }
}
Harry Bloom
sumber
3
a) tidak perlu membuat OurErrorProtocol, cukup gunakan Kesalahan penerapan CustomError secara langsung. b) ini tidak berfungsi (setidaknya di Swift 3: localizedDescription tidak pernah dipanggil dan Anda mendapatkan "Operasi tidak dapat diselesaikan."). Anda perlu menerapkan LocalizedError sebagai gantinya; lihat jawabanku.
prewett
@prewett Saya baru saja menyadarinya tetapi Anda benar! Mengimplementasikan bidang errorDescription di LocalizedError sebenarnya menetapkan pesan daripada menggunakan metode saya seperti yang dijelaskan di atas. Saya masih menyimpan pembungkus "OurErrorProtocol", karena saya membutuhkan bidang localizedTitle juga. Terima kasih telah menunjukkannya!
Harry Bloom
106

Dalam kasus Anda, kesalahannya adalah Anda mencoba membuat sebuah Errorinstance. Errordi Swift 3 adalah protokol yang dapat digunakan untuk menentukan kesalahan khusus. Fitur ini khusus untuk aplikasi Swift murni yang dijalankan pada OS yang berbeda.

Dalam pengembangan iOS, NSErrorkelas masih tersedia dan sesuai dengan Errorprotokol.

Jadi, jika tujuan Anda hanya untuk menyebarkan kode kesalahan ini, Anda dapat dengan mudah mengganti

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

dengan

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

Jika tidak memeriksa Sandeep Bhandari 's jawaban tentang bagaimana untuk membuat jenis kesalahan kustom

Luca D'Alberti
sumber
15
Saya hanya mendapatkan error: Error cannot be created because it has no accessible initializers.
Supertecnoboff
@AbhishekThapliyal bisakah Anda menjelaskan sedikit lebih banyak komentar Anda? Saya tidak mengerti apa yang Anda maksud.
Luca D'Alberti
2
@ LucaD'Alberti seperti di Swift 4 Kesalahannya yang ditampilkan tidak dapat dibuat karena tidak memiliki penginisialisasi yang dapat diakses, saat membuat Objek Kesalahan.
Maheep
1
@Maheep apa yang saya sarankan di jawaban saya bukan untuk digunakan Error, tapi NSError. Tentu saja menggunakan Errorlemparan kesalahan.
Luca D'Alberti
Kesalahan adalah protokolnya. Tidak dapat dibuat instance-nya secara langsung.
slobodans
52

Anda dapat membuat enum untuk menangani kesalahan :)

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

dan kemudian buat metode di dalam enum untuk menerima kode respons http dan mengembalikan kesalahan yang sesuai :)

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

Terakhir, perbarui blok kegagalan Anda untuk menerima parameter tunggal tipe RikhError :)

Saya memiliki tutorial mendetail tentang cara merestrukturisasi model jaringan Objective - C berbasis Object Oriented tradisional menjadi model Protocol Oriented modern menggunakan Swift3 di sini https://learnwithmehere.blogspot.in Lihat :)

Semoga membantu :)

Sandeep Bhandari
sumber
Ahh tapi bukankah ini harus membuatku menangani semua kasus secara manual? Itu adalah ketik kode kesalahan?
Rikh
Yup Anda harus: D Tetapi pada saat yang sama Anda dapat mengambil berbagai tindakan khusus untuk setiap status kesalahan :) sekarang Anda memiliki kontrol yang baik pada model kesalahan jika Anda tidak ingin melakukannya, Anda dapat menggunakan kasus 400 ... 404 {...} menangani hanya kasus umum :)
Sandeep Bhandari
Ahh ya! Terima kasih
Rikh
Dengan asumsi beberapa kode http tidak perlu mengarah ke kasus yang sama, Anda seharusnya dapat melakukan enum RikhError: Int, Error {case invalidRequest = 400} dan kemudian membuatnya RikhError (rawValue: httpCode)
Brian F Leighty
51

Anda harus menggunakan objek NSError.

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

Kemudian transmisikan NSError ke objek Error

Ahmed Lotfy
sumber
29

Detail

  • Versi Xcode 10.2.1 (10E1001)
  • Cepat 5

Solusi kesalahan pengorganisasian dalam suatu aplikasi

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

Pemakaian

//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)
}
Bodnarchuk dengan mudah
sumber
16

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

Perhatikan bahwa menerapkan Error, misalnya, seperti yang dijelaskan di salah satu jawaban, akan gagal (setidaknya di Swift 3), dan memanggil localizedDescription akan menghasilkan string "Operasi tidak dapat diselesaikan. (.StringError error 1.) "

prewett
sumber
Haruskah itu mMsg = msg
Brett
1
Ups, benar. Saya mengubah "msg" menjadi "description", yang semoga sedikit lebih jelas dari yang asli.
prewett
4
Anda dapat menguranginya menjadi struct StringError : LocalizedError { public let errorDescription: String? }, dan itu hanya digunakan sebagaiStringError(errorDescription: "some message")
Koen.
7
 let error = NSError(domain:"", code:401, userInfo:[ NSLocalizedDescriptionKey: "Invaild UserName or Password"]) as Error
            self.showLoginError(error)

buat objek NSError dan ketik ke Error, tunjukkan di mana saja

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

Saya masih berpikir bahwa jawaban Harry adalah yang paling sederhana dan lengkap tetapi jika Anda membutuhkan sesuatu yang lebih sederhana, gunakan:

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

Dan gunakan atau uji seperti ini:

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

func print(error: Error) {
    print("We have an ERROR: ", error.localizedDescription)
}
Bukit Reimond
sumber
3
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)
}
Daniel.scheibe
sumber
1

Saya tahu Anda sudah puas dengan sebuah jawaban tetapi jika Anda tertarik untuk mengetahui pendekatan yang tepat, maka ini mungkin berguna untuk Anda. Saya lebih suka untuk tidak mencampur kode kesalahan http-respon dengan kode kesalahan di objek kesalahan (bingung? Lanjutkan membaca sedikit ...).

Kode respons http adalah kode kesalahan standar tentang respons http yang menentukan situasi umum saat respons diterima dan bervariasi dari 1xx hingga 5xx (misalnya 200 OK, 408 Permintaan habis waktu, 504 Gateway timeout, dll - http://www.restapitutorial.com/ httpstatuscodes.html )

Kode kesalahan dalam objek NSError memberikan identifikasi yang sangat spesifik untuk jenis kesalahan yang dijelaskan objek untuk domain aplikasi / produk / perangkat lunak tertentu. Misalnya aplikasi Anda dapat menggunakan 1000 untuk "Maaf, Anda tidak dapat memperbarui catatan ini lebih dari sekali dalam sehari" atau mengatakan 1001 untuk "Anda memerlukan peran manajer untuk mengakses sumber daya ini" ... yang khusus untuk domain / aplikasi Anda logika.

Untuk aplikasi yang sangat kecil, terkadang kedua konsep ini digabungkan. Tetapi mereka sama sekali berbeda seperti yang Anda lihat dan sangat penting & membantu untuk merancang dan bekerja dengan perangkat lunak besar.

Jadi, ada dua teknik untuk menangani kode dengan cara yang lebih baik:

1. Callback penyelesaian akan melakukan semua pemeriksaan

completionHandler(data, httpResponse, responseError) 

2. Metode Anda menentukan situasi sukses dan kesalahan dan kemudian memanggil callback yang sesuai

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

Selamat coding :)

Tushar
sumber
Jadi pada dasarnya apa yang ingin Anda katakan adalah meneruskan parameter "data" jika ada beberapa string tertentu yang akan ditampilkan jika ada kode kesalahan tertentu yang dikembalikan dari server? (Maaf, saya terkadang bisa sedikit lambat!)
Rikh