Как правильно определить, какая базовая ошибка используется в Swift при выдаче ошибок, которые ее расширяют? - PullRequest
0 голосов
/ 17 апреля 2019

Я ищу, как узнать, что ошибка моего типа, AppError.

У меня в основном есть класс, определенный как class AppError<ErrorType>: Error {.

Я тогда делаю

enum MyError {
    case noApp
    case noLabel
}

Мой бросок похож на throw AppError(type: MyError.noApp)

После улова я запускаю метод, в котором у меня возникают проблемы ...

static func handle(error: Error) {
    print("App Error handled")
    print(error);
    let mine = error is AppError<Any>;
    // This is always false
    print(mine);
}

Я пробовал Any и AnyObject как Общий, но это всегда ложно.Печать error всегда выглядит так: myapp.AppError<myapp.MyError>

Я хочу знать, что это AppError, поэтому я могу вызывать методы, специфичные для него.

На практике я мог бы иметьтакие ошибки, как ConnectionError, CreateError и т. д. Все это зависит от AppError.Я хочу, чтобы мой обработчик знал, что переданная ошибка основана на AppError, который будет иметь свои собственные пользовательские свойства по сравнению с общей ошибкой из сторонней библиотеки, которая не будет иметь этих свойств.

Update Поскольку это может быть невозможно сделать, я попробовал следующее:

class ErrorHandler {
    static func handle(error: Error) {
        print("App Error handled")
        print(error);
        switch error {
            // List all AppError<Types> here since <Any> won't work
            case is AppError<MyError>:
                self._handleAppError(error: error)
            default:
                print("Generic Error")
                print(error);
        }
    }

    private static func _handleAppError(error: AppError<Any>) {
        print("My error!");
        print(error);
        print(error.type);
    }
}

Проблема в том, что он жалуется Cannot convert value of type 'Error' to expected argument type 'AppError<Any>, даже если он находится в строке, которая должна быть такого типа.

Ответы [ 3 ]

0 голосов
/ 18 апреля 2019

Я разработал свою собственную логику обработки ошибок на стороне API, аналогичную той, которую вы ищете.Надеюсь, это поможет.

APIError

public enum APIError: Error {

    public enum ResponseFailureReason {

        case castingFailure(description: String)
        case badResponse(statusCode: Int?, error: Error?)
        case jsonDecodingFailure(error: DecodingError?)
        case emptyResponse(description: String)
        case authenticationFailed
        case unknownFailure(error: Error)
        case networkReachabilityFailure
    }

    case invalidURL(url: String)
    case responseFailed(reason: ResponseFailureReason)
    case noInternetConnection(reason: ResponseFailureReason)
}

Вспомогательные методы APIError

extension APIError {

    public var isInvalidURLError: Bool {
        if case .invalidURL = self {
            return true
        }

        return false
    }

    public var isResponseFailureReason: Bool {
        if case .responseFailed = self {
            return true
        }

        return false
    }

    public var isNetworkReachabilityError: Bool {
        if case .noInternetConnection = self {
            return true
        }

        return false
    }

    public var isAuthenticationFailed: Bool {
        if self.isResponseFailureReason,
            case let .responseFailed(reason) = self,
            case .authenticationFailed = reason {

            return true
        }

        return false
    }
}

Вот каквыдается ошибка.

let failureReason: APIError.ResponseFailureReason = .jsonDecodingFailure(error: decodingError)
throw APIError.responseFailed(reason: failureReason)

throw APIError.responseFailed(reason: .badResponse(statusCode: statusCode, error: error))

throw APIError.responseFailed(reason: .authenticationFailed)

Вот как ошибки идентифицируются и обрабатываются

func handleAuthFailure(_ err: APIError?) -> Bool {
    if let error = err {
        if error.isAuthenticationFailed {
            print("Received Authentication failure")
            return true
        }
    }

    return false
}

func handleNetworkReachabilityFailure(_ err: APIError?) -> Bool {
    if let error = err {
        if error.isNetworkReachabilityError,
            case let .noInternetConnection(reason) = error,
            case .networkReachabilityFailure = reason {

            return true
        }
    }

    return false
}

func handleBadResponse(for error: Error) {

    if let error = error as? APIError,
        error.isResponseFailureReason,
        case let .responseFailed(reason) = error,
        case let .badResponse(statusCode, _) = reason {

        if statusCode == HTTPStatusCode.forbidden.rawValue {
            // handle Forbidden case
            return
        } else if statusCode == HTTPStatusCode.conflict.rawValue {
            // handle conflict case
            return
        }
    }
}
0 голосов
/ 18 апреля 2019

Документы Swift дают понять, что следует попытаться использовать enum.Это делает решения для моей цели слишком сложными.В итоге я создал базовый класс, а затем расширил его для своих различных типов ошибок.

Сначала я настроил свой базовый класс ошибок.

typealias AppErrorContext = [String:Any]?;

class AppError: Error {
    public let message: String;
    public let context: AppErrorContext;
    public let line: Int;
    public let function: String;
    public let file: String;

    init(
        message: String,
        context: AppErrorContext = [:],
        line: Int = #line,
        file: String = #file,
        function: String = #function
        ) {
        self.message = message;
        self.context = context;
        self.line = line;
        self.function = function;
        self.file = file;
    }
}

Затем я создал несколько классов, таких как:

// class names here are for demo purpose only
class EntityMissingInfoError: AppError {}
class EntityNotFoundError: AppError {}
class ConnectionUnableToCompleteError: AppError {}
class ConnectionNoResponseError: AppError {}

Если я могу затем бросить, как throw EntityMissingInfoError(message: "Something was missing!", context: ["expected": "id", "received": "notid")

Мои вызовы тогда будут выглядеть как

do {
  try methodThatThrows();
}
catch is EntityMissingInfoError {
  // do nothing / ignore
}
catch let error {
  ErrorHandler.handle(error: error);
}

Мой ErrorHandler будет тогда просто таким:

struct ErrorHandler {
    static func handle(error: Error) {
        var message: String;
        var line: Int = 0;
        var file: String = "[Unknown]";
        var function: String = "[Unknown]";
        var context: AppErrorContext = [:];

        if let appError = error as? AppError {
            message = appError.message;
            line = appError.line;
            file = appError.file;
            function = appError.function;
            context = appError.context;

        } else {
            message = error.localizedDescription;
        }

        let logContext: [String:Any] = [
            "type": String(describing: error),
            "line": line,
            "function": function,
            "file": file,
            "context": context ?? [:]
        ]

        // in practice, logging to service rather than printing
        print(message);
        print(logContext);
    }
}

Обработчик ошибок теперь может выполнять специальную логику для любых моих ошибок, сохраняя при этом случай по умолчанию для общих ошибок, генерируемых из других библиотек.

0 голосов
/ 18 апреля 2019

Я видел из другого вашего поста, что ваша ошибка должна быть более гибкой, чем обычные перечисления, поэтому я обновил ее, чтобы сделать это, все еще используя протоколы:

Я думаю, что вы могли бы добиться этого с помощью протоколоввместо:

protocol AppErrorProtocol: Error {
    func handle()
}

struct AppError<ErrorType>: AppErrorProtocol {
    let error: ErrorType
    init(error: ErrorType) {
        self.error = error
    }
    func handle() {
        print(error)
    }
}

enum MyErrors {
    case one
}

func handleError(_ error: Error) {
    print(type(of: error))
    switch error {
        case let error as AppErrorProtocol:
          error.handle()
        default:
          print("Some other error \(error)")
    }
}

handleError(AppError(error: MyErrors.one))
handleError(AppError(error: "foo"))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...