स्विफ्ट में त्रुटि प्रकार के साथ स्थानीयकृत विवरण कैसे प्रदान करें?


202

मैं स्विफ्ट 3 सिंटैक्स के साथ एक कस्टम त्रुटि प्रकार को परिभाषित कर रहा हूं और मैं उस त्रुटि का उपयोगकर्ता-अनुकूल विवरण प्रदान करना चाहता हूं जो ऑब्जेक्ट की localizedDescriptionसंपत्ति द्वारा वापस किया जाता है Error। मैं यह कैसे कर सकता हूं?

public enum MyError: Error {
  case customError

  var localizedDescription: String {
    switch self {
    case .customError:
      return NSLocalizedString("A user-friendly description of the error.", comment: "My error")
    }
  }
}

let error: Error = MyError.customError
error.localizedDescription
// "The operation couldn’t be completed. (MyError error 0.)"

क्या localizedDescriptionमेरे कस्टम त्रुटि विवरण ("त्रुटि के उपयोगकर्ता के अनुकूल विवरण") को वापस करने का कोई तरीका है ? ध्यान दें कि यहाँ त्रुटि ऑब्जेक्ट प्रकार का है Errorऔर नहीं MyError। मैं निश्चित रूप से, MyError को ऑब्जेक्ट कास्ट कर सकता हूं

(error as? MyError)?.localizedDescription

लेकिन क्या यह मेरी त्रुटि प्रकार के लिए कास्टिंग के बिना काम करने का एक तरीका है?

जवाबों:


401

जैसा कि Xcode 8 बीटा 6 रिलीज नोट में वर्णित है,

स्विफ्ट-डिफाइन्ड एरर टाइप्स नए लोकलाइज्डइरर प्रोटोकॉल को अपनाकर स्थानीयकृत त्रुटि विवरण प्रदान कर सकते हैं।

आपके मामले में:

public enum MyError: Error {
    case customError
}

extension MyError: LocalizedError {
    public var errorDescription: String? {
        switch self {
        case .customError:
            return NSLocalizedString("A user-friendly description of the error.", comment: "My error")
        }
    }
}

let error: Error = MyError.customError
print(error.localizedDescription) // A user-friendly description of the error.

यदि त्रुटि परिवर्तित हो जाती है NSError(जो हमेशा संभव है) तो आप और भी अधिक जानकारी प्रदान कर सकते हैं :

extension MyError : LocalizedError {
    public var errorDescription: String? {
        switch self {
        case .customError:
            return NSLocalizedString("I failed.", comment: "")
        }
    }
    public var failureReason: String? {
        switch self {
        case .customError:
            return NSLocalizedString("I don't know why.", comment: "")
        }
    }
    public var recoverySuggestion: String? {
        switch self {
        case .customError:
            return NSLocalizedString("Switch it off and on again.", comment: "")
        }
    }
}

let error = MyError.customError as NSError
print(error.localizedDescription)        // I failed.
print(error.localizedFailureReason)      // Optional("I don\'t know why.")
print(error.localizedRecoverySuggestion) // Optional("Switch it off and on again.")

CustomNSErrorप्रोटोकॉल को अपनाने से त्रुटि एक userInfoशब्दकोश (और भी domainऔर code) प्रदान कर सकती है । उदाहरण:

extension MyError: CustomNSError {

    public static var errorDomain: String {
        return "myDomain"
    }

    public var errorCode: Int {
        switch self {
        case .customError:
            return 999
        }
    }

    public var errorUserInfo: [String : Any] {
        switch self {
        case .customError:
            return [ "line": 13]
        }
    }
}

let error = MyError.customError as NSError

if let line = error.userInfo["line"] as? Int {
    print("Error in line", line) // Error in line 13
}

print(error.code) // 999
print(error.domain) // myDomain

7
वहाँ एक कारण है कि आप बनाते हैं MyErrorएक Errorपहले और के साथ इसे विस्तार LocalizedErrorबाद में? यदि आपने इसे LocalizedErrorपहली जगह में बनाया है तो क्या कोई अंतर है?
जी.ई.

9
@ Gee.E: इससे कोई फर्क नहीं पड़ता। यह कोड को व्यवस्थित करने का एक तरीका है (प्रत्येक प्रोटोकॉल के लिए एक एक्सटेंशन)। की तुलना करें stackoverflow.com/questions/36263892/... , stackoverflow.com/questions/40502086/... , या natashatherobot.com/using-swift-extensions
मार्टिन आर

4
आह, जाँच करें। मुझे वही मिल रहा है जो आप अभी कह रहे हैं। Natashatherobot.com/using-swift-extensions पर "प्रोटोकॉल अनुरूपता" अनुभाग वास्तव में आपके लिए क्या अर्थ है, इसका एक अच्छा उदाहरण है। धन्यवाद!
जी.ई.

1
@MartinR यदि मेरी त्रुटि NSError में परिवर्तित हो जाएगी, तो मैं त्रुटि से एक शब्दकोश कैसे पारित कर सकता हूं जिसे NSError के userInfo के रूप में एक्सेस किया जा सकता है?
बैंगऑपरेटर

18
var errorDescription: String?इसके बजाय टाइप करने के लिए सावधान रहें String। LocalizedError के कार्यान्वयन में एक बग है। SR-5858 देखें ।
एतानहुंग १३

35

मैं यह भी जोड़ूंगा, अगर आपकी त्रुटि में इस तरह के पैरामीटर हैं

enum NetworkError: LocalizedError {
  case responseStatusError(status: Int, message: String)
}

आप इन मापदंडों को अपने स्थानीयकृत विवरण में इस तरह से कॉल कर सकते हैं:

extension NetworkError {
  public var errorDescription: String? {
    switch self {
    case .responseStatusError(status: let status, message: let message):
      return "Error with status \(status) and message \(message) was thrown"
  }
}

आप इसे इस तरह छोटा भी कर सकते हैं:

extension NetworkError {
  public var errorDescription: String? {
    switch self {
    case let .responseStatusError(status, message):
      return "Error with status \(status) and message \(message) was thrown"
  }
}

4

अब दो एरर-एडॉप्टिंग प्रोटोकॉल हैं, जो ऑब्जेक्टिव-सी के लिए अतिरिक्त जानकारी प्रदान करने के लिए आपका एरर टाइप अपना सकते हैं - लोकलाइज्डइरॉयर और कस्टमएनएसइयर। यहाँ एक उदाहरण त्रुटि है जो दोनों को अपनाती है:

enum MyBetterError : CustomNSError, LocalizedError {
    case oops

    // domain
    static var errorDomain : String { return "MyDomain" }
    // code
    var errorCode : Int { return -666 }
    // userInfo
    var errorUserInfo: [String : Any] { return ["Hey":"Ho"] };

    // localizedDescription
    var errorDescription: String? { return "This sucks" }
    // localizedFailureReason
    var failureReason: String? { return "Because it sucks" }
    // localizedRecoverySuggestion
    var recoverySuggestion: String? { return "Give up" }

}

2
क्या आप एक संपादन कर सकते हैं? आपके उदाहरण प्रत्येक के मूल्य को समझने में ज्यादा मदद नहीं करते हैं। या बस इसे डिलीट कर दें क्योंकि मार्टिन का जवाब बिल्कुल यही प्रदान करता है ...
Honey

3

एक संरचना का उपयोग करना एक विकल्प हो सकता है। स्थैतिक स्थानीयकरण के साथ थोड़ा सा लालित्य:

import Foundation

struct MyError: LocalizedError, Equatable {

   private var description: String!

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

   var errorDescription: String? {
       return description
   }

   public static func ==(lhs: MyError, rhs: MyError) -> Bool {
       return lhs.description == rhs.description
   }
}

extension MyError {

   static let noConnection = MyError(description: NSLocalizedString("No internet connection",comment: ""))
   static let requestFailed = MyError(description: NSLocalizedString("Request failed",comment: ""))
}

func throwNoConnectionError() throws {
   throw MyError.noConnection
}

do {
   try throwNoConnectionError()
}
catch let myError as MyError {
   switch myError {
   case .noConnection:
       print("noConnection: \(myError.localizedDescription)")
   case .requestFailed:
       print("requestFailed: \(myError.localizedDescription)")
   default:
      print("default: \(myError.localizedDescription)")
   }
}

0

यहाँ और अधिक सुंदर समाधान है:

  enum ApiError: String, LocalizedError {

    case invalidCredentials = "Invalid credentials"
    case noConnection = "No connection"

    var localizedDescription: String { return NSLocalizedString(self.rawValue, comment: "") }

  }

4
यह रनटाइम के दौरान अधिक सुरुचिपूर्ण हो सकता है, लेकिन स्थैतिक स्थानीयकरण कदम अनुवादकों के लिए इन तारों को निकालने में विफल रहेगा; "Bad entry in file – Argument is not a literal string"जब आप चलते हैं exportLocalizationsया genstringsअपनी अनुवाद योग्य पाठ की सूची बनाते हैं तो आपको एक त्रुटि दिखाई देगी ।
सेविनोला

@ सविनोला सहमत हैं, ऐसे मामले में स्थैतिक स्थानीयकरण काम नहीं करेगा। शायद उपयोग switch + caseकेवल विकल्प है ...
विटाली गोज़ेनको

कच्चे मूल्यों का उपयोग करने से आपकी किसी भी त्रुटि के लिए संबंधित मूल्यों के उपयोग को रोका जा सकेगा
Brody Robertson
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.