У меня есть довольно большой объем кода Swift 3 для Mac OS 10.11 и выше (с использованием Xcode 8.2.1). Существует ряд процессов, среди которых приложение с графическим интерфейсом и фоновая служба. Оба они используют пользовательский URLProtocol
, который реализован в рамках (импортируется как приложением, так и службой). Иногда протокол может генерировать экземпляры enum
, которые соответствуют Error
, которые он перехватывает и обрабатывает соответствующим образом (обычно с помощью URLProtocolClient для передачи их в URLSession, пытающийся выполнить запрос).
- Когда ошибки нет, и приложение, и служба работают нормально.
- Когда возникает есть ошибка, приложение работает нормально (ну, как и ожидалось).
- При возникновении ошибки служба аварийно завершает работу.
Блуждание по отладчику показало, что этот сбой происходит, когда Error
автоматически преобразуется во время выполнения в NSError
. Я явно добавил это приведение в свой код, и, конечно же, я получаю тот же сбой, теперь в этой строке.
Я видел Swift 3.1: сбой, когда пользовательская ошибка преобразуется в NSError для доступа к свойству домена, но здесь это не применяется:
- Решение — расширение
Error
доCustomNSError
(иLocalizedError
на всякий случай) и реализация соответствующих свойств — не помогло. - Сбой происходит после получения
domain
; насколько я могу судить, для меня это не было проблемой. - Возможно актуально: это было на iOS, а не на Mac.
Уже попробовав единственное перечисленное решение этого вопроса, я немного растерялся. Я отлаживал это часами, но ничего не добился, за исключением того, что это, кажется, происходит где-то глубоко внутри dyld
.
Код:
enum CrowbarProtocolError: Error {
case FailedToCreateSocket;
case PathTooLong;
case NonSocketFile;
case SocketNotFound;
...
case UnrecognizedError(errno: Int32);
}
extension CrowbarProtocolError: LocalizedError {
public var errorDescription: String? {
return "Some localized description"
}
}
extension CrowbarProtocolError: CustomNSError {
public static var errorDomain: String {
return "Some Domain Name"
}
public var errorCode: Int {
return 204 //Should be your custom error code.
}
public var errorUserInfo: [String: Any] {
return ["WTF": "Swift?"];
}
}
...
open class CrowbarUrlProtocol: URLProtocol {
...
override open func startLoading() {
do {
let sockHandle = try CrowbarUrlProtocol.openSocket();
let req = try buildRequestData();
sockHandle.write(req);
NotificationCenter.default.addObserver(
self,
selector: #selector(self.readCompleted),
name: .NSFileHandleReadToEndOfFileCompletion,
object: nil);
sockHandle.readToEndOfFileInBackgroundAndNotify();
} catch let err {
Log.warn("CrowbarUrlProtocol request failed with error \(err)");
// -- In the background service, THIS LINE CRASHES! --
let err2 = err as NSError;
Log.warn("As NSError: \(err2)");
// -- Without the explicit cast, it crashes on this line --
self.client?.urlProtocol(self, didFailWithError: err);
}
}
...
}
У меня есть одна идея для решения этой проблемы: просто делать все (или, насколько это возможно), используя NSError
s, на том основании, что если никогда не будет необходимости конвертировать Error
в NSError
, то что бы ни вызывало этот сбой, не произойдет. Не знаю, получится ли, но попробовать стоит...