Konwersja ErrorType na NSError powoduje utratę powiązanych obiektów
W Swift 2.0 NSError
jest zgodny z protokołem ErrorType
.
Dla niestandardowo zdefiniowanego błędu, możemy określić obiekt(y) powiązujący (- E) w niektórych przypadkach, jak poniżej.
enum LifeError: ErrorType {
case BeBorn
case LostJob(job: String)
case GetCaughtByWife(wife: String)
...
}
Możemy wygodnie wykonać następujące czynności:
do {
try haveAffairWith(otherPerson)
} catch LifeError.GetCaughtByWife(let wife) {
...
}
Jeśli jednak chcemy, aby przeszła w inne miejsca jako NSError
, traci informacje o obiektach powiązanych.
println("\(LifeError.GetCaughtByWife("Name") as NSError)")
Druki:
Error Domain=... Code=1 "The operation couldn't be completed". (... error 1)
I jego userInfo
jest nil
.
Gdzie moje wife
jest związane z ErrorType
?
7 answers
Nowość w Xcode 8: CustomNSError
protokół .
enum LifeError: CustomNSError {
case beBorn
case lostJob(job: String)
case getCaughtByWife(wife: String)
static var errorDomain: String {
return "LifeError"
}
var errorCode: Int {
switch self {
case .beBorn:
return 0
case .lostJob(_):
return 1
case .getCaughtByWife(_):
return 2
}
}
var errorUserInfo: [String : AnyObject] {
switch self {
case .beBorn:
return [:]
case .lostJob(let job):
return ["Job": job]
case .getCaughtByWife(let wife):
return ["Wife": wife]
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-30 17:21:20
An ErrorType
naprawdę nie można rzucić na NSError
, musisz wziąć Powiązane Dane i spakować je do NSError
.
do {
try haveAffairWith(otherPerson)
} catch LifeError.GetCaughtByWife(let wife) {
throw NSError(domain:LifeErrorDomain code:-1 userInfo:
[NSLocalizedDescriptionKey:"You cheated on \(wife)")
}
EDIT: w rzeczywistości możesz wykonać cast od ErrorType
do NSError
, ale NSError
otrzymany z domyślnej implementacji jest dość prymitywny. To, co robię w mojej aplikacji, to zaczepianie aplikacji: willPresentError: w delegacie aplikacji i używanie niestandardowej klasy, aby odczytać ErrorType
mojej aplikacji i udekorować NSErrors, aby powrócić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-28 19:53:22
Tworzenie NSError
w każdym bloku catch może prowadzić do dużej ilości kopiowania i wklejania, aby przekonwertować własne ErrorType
na NSError
. Usunąłem to podobnie jak @ powertoold.
protocol CustomErrorConvertible {
func userInfo() -> Dictionary<String,String>?
func errorDomain() -> String
func errorCode() -> Int
}
To rozszerzenie może zawierać kod, co jest wspólne dla LifeError
, które już mamy i innych niestandardowych typów błędów, które możemy utworzyć.
extension CustomErrorConvertible {
func error() -> NSError {
return NSError(domain: self.errorDomain(), code: self.errorCode(), userInfo: self.userInfo())
}
}
Do realizacji!
enum LifeError: ErrorType, CustomErrorConvertible {
case BeBorn
case LostJob(job: String)
case GetCaughtByPolice(police: String)
func errorDomain() -> String {
return "LifeErrorDomain"
}
func userInfo() -> Dictionary<String,String>? {
var userInfo:Dictionary<String,String>?
if let errorString = errorDescription() {
userInfo = [NSLocalizedDescriptionKey: errorString]
}
return userInfo
}
func errorDescription() -> String? {
var errorString:String?
switch self {
case .LostJob(let job):
errorString = "fired as " + job
case .GetCaughtByPolice(let cops):
errorString = "arrested by " + cops
default:
break;
}
return errorString
}
func errorCode() -> Int {
switch self {
case .BeBorn:
return 1
case .LostJob(_):
return -9000
case .GetCaughtByPolice(_):
return 50
}
}
}
I tak go używać.
func lifeErrorThrow() throws {
throw LifeError.LostJob(job: "L33tHax0r")
}
do {
try lifeErrorThrow()
}
catch LifeError.BeBorn {
print("vala morgulis")
}
catch let myerr as LifeError {
let error = myerr.error()
print(error)
}
Możesz łatwo przenieść niektóre funkcje, takie jak func userInfo() -> Dictionary<String,String>?
z LifeError
do extension CustomErrorConvertible
lub innego rozszerzenia.
Zamiast kodowania na twardo kody błędów, jak powyżej, enum może być lepsze.
enum LifeError:Int {
case Born
case LostJob
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-30 17:23:55
Moim rozwiązaniem tego problemu było stworzenie enum zgodnego z Int, ErrorType:
enum AppError: Int, ErrorType {
case UserNotLoggedIn
case InternetUnavailable
}
A następnie rozszerzyć enum, aby było zgodne z CustomStringConvertible i niestandardowym protokołem o nazwie CustomErrorConvertible:
extension AppError: CustomStringConvertible, CustomErrorConvertible
protocol CustomErrorConvertible {
var error: NSError { get }
}
Dla opisu i błędu włączyłem AppError. Przykład:
Description: switch self {
case .UserNotLoggedIn: return NSLocalizedString("ErrorUserNotLoggedIn", comment: "User not logged into cloud account.")
case .InternetUnavailable: return NSLocalizedString("ErrorInternetUnavailable", comment: "Internet connection not available.")
}
Error: switch self {
case .UserNotLoggedIn: errorCode = UserNotLoggedIn.rawValue; errorDescription = UserNotLoggedIn.description
case .InternetUnavailable: errorCode = InternetUnavailable.rawValue; errorDescription = InternetUnavailable.description
}
A potem skomponowałem swój własny NSError:
return NSError(domain:NSBundle.mainBundle().bundleIdentifier!, code:errorCode, userInfo:[NSLocalizedDescriptionKey: errorDescription])
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-04 17:48:47
Ja też mam ten problem używając PromiseKit i znalazłem obejście, które może być trochę brzydkie, ale wydaje się działać.
Wklejam tutaj mój plac zabaw, abyś mógł zobaczyć cały proces.
import Foundation
import PromiseKit
import XCPlayground
let error = NSError(domain: "a", code: 1, userInfo: ["hello":"hello"])
// Only casting won't lose the user info
let castedError = error as ErrorType
let stillHaveUserInfo = castedError as NSError
// when using promises
func convert(error: ErrorType) -> Promise<Int> {
return Promise<Int> {
(fulfill, reject) in
reject(error)
}
}
let promiseA = convert(error)
// Seems to lose the user info once we cast back to NSError
promiseA.report { (promiseError) -> Void in
let lostUserInfo = promiseError as NSError
}
// Workaround
protocol CastingNSErrorHelper {
var userInfo: [NSObject : AnyObject] { get }
}
extension NSError : CastingNSErrorHelper {}
promiseA.report { (promiseError) -> Void in
let castingNSErrorHelper = promiseError as! CastingNSErrorHelper
let recoveredErrorWithUserInfo = castingNSErrorHelper as! NSError
}
XCPSetExecutionShouldContinueIndefinitely()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-13 16:08:09
Najlepszym rozwiązaniem, jakie znalazłem, jest posiadanie Objective - C wrappera do odlewania ErrorType
do NSError
(przez NSObject*
parmetr) i ekstrakcji userInfo
. Najprawdopodobniej zadziała to również w przypadku innych powiązanych obiektów.
W moim przypadku wszystkie inne próby użycia tylko Swifta zaowocowały uzyskaniem nil
userInfo
.
Oto Helper Objective-C. Umieść go na przykład w MyErrorUtils
klasie narażonej na Swift:
+ (NSDictionary*)getUserInfo:(NSObject *)error {
NSError *nsError = (NSError *)error;
if (nsError != nil) {
return [nsError userInfo];
} else {
return nil;
}
}
Następnie użyj helpera w języku Swift w następujący sposób:
static func myErrorHandler(error: ErrorType) {
// Note the as? cast to NSObject
if let userInfo: [NSObject: AnyObject]? =
MyErrorUtils.getUserInfo(error as? NSObject) {
let myUserInfo = userInfo["myCustomUserInfo"]
// ... Error processing based on userInfo ...
}
}
(I ' m obecnie używa XCode 8 i Swift 2.3)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-28 12:59:23
Jak wskazała przyjęta odpowiedź, jest teraz CustomNSError
W Swift 3, jednak niekoniecznie musisz go używać. Jeśli zdefiniujesz swój typ błędu w ten sposób
@objc
enum MyErrorType: Int, Error { ... }
Wtedy ten błąd można bezpośrednio rzucić na NSError
:
let error: MyErrorType = ...
let objcError = error as NSError
Właśnie odkryłem to dzisiaj i choć dzielę się tym ze światem.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-30 18:01:29