ErrorType을 NSError로 변환하면 관련 객체가 손실됩니다.
Swift 2.0에서는 프로토콜을 NSError
따릅니다 ErrorType
.
사용자 정의 오류의 경우 아래와 같이 경우에 따라 연관 객체를 지정할 수 있습니다.
enum LifeError: ErrorType {
case BeBorn
case LostJob(job: String)
case GetCaughtByWife(wife: String)
...
}
다음을 편안하게 수행 할 수 있습니다.
do {
try haveAffairWith(otherPerson)
} catch LifeError.GetCaughtByWife(let wife) {
...
}
그러나으로 다른 위치로 전달하려는 경우 NSError
관련 객체 정보가 손실됩니다.
println("\(LifeError.GetCaughtByWife("Name") as NSError)")
인쇄물:
Error Domain=... Code=1 "The operation couldn't be completed". (... error 1)
그리고 그것 userInfo
입니다 nil
.
내는 어디에 wife
연결되어 ErrorType
있습니까?
Xcode 8의 새로운 기능 : CustomNSError
프로토콜 .
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]
}
}
}
가 ErrorType
정말로 주조 할 수없는 NSError
, 당신은 관련 데이터를 받아와로 패키지해야 NSError
자신을.
do {
try haveAffairWith(otherPerson)
} catch LifeError.GetCaughtByWife(let wife) {
throw NSError(domain:LifeErrorDomain code:-1 userInfo:
[NSLocalizedDescriptionKey:"You cheated on \(wife)")
}
편집 : 실제로에서 ErrorType
으로 캐스트 할 수 NSError
있지만 NSError
기본 구현에서 얻는 것은 매우 원시적입니다. 내 앱에서 내가하는 일은 내 앱 델리게이트에서 application : willPresentError : 후킹하고 사용자 지정 클래스를 사용하여 내 앱을 읽고 ErrorType
NSErrors를 장식하여 반환하는 것입니다.
NSError
모든 catch 블록에서 생성하면 사용자 정의 ErrorType
를 NSError
. @powertoold 와 비슷하게 추상화했습니다 .
protocol CustomErrorConvertible {
func userInfo() -> Dictionary<String,String>?
func errorDomain() -> String
func errorCode() -> Int
}
이 확장은 LifeError
우리가 이미 가지고 있는 일반적인 코드 와 우리가 만들 수있는 다른 사용자 정의 오류 유형을 보유 할 수 있습니다.
extension CustomErrorConvertible {
func error() -> NSError {
return NSError(domain: self.errorDomain(), code: self.errorCode(), userInfo: self.userInfo())
}
}
구현을 시작하십시오!
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
}
}
}
그리고 이것이 그것을 사용하는 방법입니다.
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)
}
당신은 쉽게와 같은 특정 기능을 움직일 수 func userInfo() -> Dictionary<String,String>?
에서 LifeError
에 extension CustomErrorConvertible
또는 다른 확장.
위와 같이 오류 코드를 하드 코딩하는 대신 열거 형이 선호 될 수 있습니다.
enum LifeError:Int {
case Born
case LostJob
}
이 문제에 대한 내 해결책은 Int, ErrorType을 준수하는 열거 형을 만드는 것입니다.
enum AppError: Int, ErrorType {
case UserNotLoggedIn
case InternetUnavailable
}
그런 다음 열거 형을 확장하여 CustomStringConvertible 및 CustomErrorConvertible이라는 사용자 지정 프로토콜을 준수합니다.
extension AppError: CustomStringConvertible, CustomErrorConvertible
protocol CustomErrorConvertible {
var error: NSError { get }
}
설명과 오류에 대해 AppError를 켰습니다. 예:
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
}
그런 다음 내 자신의 NSError를 작성했습니다.
return NSError(domain:NSBundle.mainBundle().bundleIdentifier!, code:errorCode, userInfo:[NSLocalizedDescriptionKey: errorDescription])
PromiseKit을 사용 하여이 문제가 발생하고 약간 추악 할 수 있지만 작동하는 것처럼 보이는 해결 방법을 찾았습니다.
여기에 내 놀이터를 붙여 넣어 전체 과정을 볼 수 있습니다.
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()
The best solution that I found, is to have an Objective-C wrapper for casting the ErrorType
to NSError
(via NSObject*
parmeter) and extracting the userInfo
. Most likely this would work for other associated objects too.
In my case all other attempts using only Swift resulted in getting a nil
userInfo
.
Here is the Objective-C helper. Place it for example in a MyErrorUtils
class exposed to Swift:
+ (NSDictionary*)getUserInfo:(NSObject *)error {
NSError *nsError = (NSError *)error;
if (nsError != nil) {
return [nsError userInfo];
} else {
return nil;
}
}
Then use the helper in Swift like this:
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 currently using XCode 8 and Swift 2.3)
As the accepted answer pointed out, there's now CustomNSError
in Swift 3, however, you don't necessarily need to use it. If you define your error type like this
@objc
enum MyErrorType: Int, Error { ... }
Then this error can directly be casted to NSError
:
let error: MyErrorType = ...
let objcError = error as NSError
I just discovered that today and though I share it with the world.
ReferenceURL : https://stackoverflow.com/questions/31422005/converting-errortype-to-nserror-loses-associated-objects
'program tip' 카테고리의 다른 글
Mysql Workbench GUI에서 테이블 내용을 보는 방법은 무엇입니까? (0) | 2020.12.25 |
---|---|
SELECT… FOR XML PATH ( ''), 1,1)의 의미는 무엇입니까? (0) | 2020.12.25 |
--save 플래그는 npm install로 무엇을합니까? (0) | 2020.12.25 |
WPF 사용자 인터페이스를 테스트하는 방법은 무엇입니까? (0) | 2020.12.25 |
Java에서 LinkageErrors를 처리하는 방법은 무엇입니까? (0) | 2020.12.25 |