При сбое издателя объединение не завершается - PullRequest
1 голос
/ 19 января 2020

В настоящее время я создаю приложение SwiftUI и использую сетевую библиотеку от github ( GitHub / Squid ) для реализации вызовов API.

Работает нормально, за исключением случаев, когда запрос не выполняется, тогда внутренние отладочные сообщения библиотеки указывают на ошибку (404 в моем тесте), но подписчик Combine никогда не получает сигнал завершения.

Я создал новый запрос, который попытается извлечь и декодировать пользователей "jsonplaceholder.typicode.com".

import Foundation
import Squid

struct GetUsersFromMockApiRequest: JsonRequest {
    typealias Result = [User]

    var routes: HttpRoute {
        ["users"]
    }
}

Пользователь - это простая структура:

struct User: Decodable {
    let id: Int
    let username: String
    let name: String
}

В моем файле ApiRequestManager.swift я объявил функцию для запроса планирования:

public func getUsersFromMockApi() -> Response<GetUsersFromMockApiRequest> {
    return GetUsersFromMockApiRequest().schedule(with: ApiService(apiUrl: "jsonplaceholder.typicode.com"))
}

И, на мой взгляд, где я хочу вызвать API, у меня есть отменяемый набор и экземпляр ApiRequestManager.

@State private var cancellableSet: Set<AnyCancellable> = []
private let requestManager = ApiRequestManager()

Теперь последним шагом является вызов функции нажатием кнопки, которая выполняет следующий код:

self.requestManager.getUsersFromMockApi()
  .print("Debug:")
  .sink(receiveCompletion: { completion in
    switch completion {
    case .failure:
      print("ApiCall failed.")
    case .finished:
      print("ApiCall finished.")
    }
  }) { result in
    print("Received users from apiCall: \(result)")
  }
  .store(in: &cancellableSet)

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

Я пытался напечатать каждое сообщение, полученное подписчиком, breakOnError, а также пошагово следовал за библиотекой, чтобы выяснить, даже выдает ошибку. Он правильно проверяет код ответа HTTP на соответствие принятым кодам состояния в функции schedule () в файле NetworkScheduler.swift и выдает пользовательскую ошибку throw Squid.Error.requestFailed(statusCode: statusCode, response: response.data) в строке 285.

Однако при отсутствии отладки и объединения Знание, что мне тяжело после броска потом.

1 Ответ

2 голосов
/ 20 января 2020

Проблема, с которой вы сталкиваетесь, может заключаться в том, что 404 не считается ошибкой из-за лежащих в основе библиотек Какао (в частности, URLSession). Это похоже на то, что делается (или нет) в библиотеке Squid, и вы реагируете на то, что она предоставляет.

Сам по себе URLSession выдает ошибки только тогда, когда не может получить ответ от сервера - поэтому, когда Проблемы связаны с невозможностью разрешить имя хоста или установить соединение. Если вы делаете соединение и получаете ответ, URLSession не выдаст ошибку, но код состояния будет правильно отображен - он просто «будет выглядеть» как успешный запрос.

В приведенном выше примере не показывать этот раздел, но что происходит с ответом 404 от URLSession, так это то, что запрос завершается, и в ответе кодируется код состояния 404, но он не генерируется как состояние ошибки.

Типичная схема решения этой проблемы в цепочке издателей Combine заключается в передаче данных результата из URLSession в оператор tryMap, где вы можете провести дополнительную проверку результата (код состояния, данные и т. Д. c) и определить если вы хотите превратить это в выданную ошибку (например, если вы получите ответ 404).

Пример такого шаблона можно найти по адресу https://heckj.github.io/swiftui-notes/#patterns -datataskpublisher-trymap . Суть этого паттерна позволяет вам определять любую ошибку, которую вы хотите (если в этих случаях выдается исключение) на основе проверки результата.

Вы можете найти другие примеры использования Combine (наряду с URLSession.dataTaskPublisher) в Использование комбайна .

...