Swift Combine: превратить ошибку в вывод издателя - PullRequest
0 голосов
/ 28 апреля 2020

Я пытаюсь провести рефакторинг некоторого кода, в котором в настоящее время я смешиваю издателя Combine с закрытием обратного вызова.

Код выполняет запрос на вход в конечную точку REST. В случае сбоя я хочу, чтобы вызывающая функция получала полезную нагрузку (вызов), в случае успеха вызывающей стороне ничего делать не нужно. Так что это немного странная ситуация, когда вызывающая сторона заинтересована ТОЛЬКО в ошибке.

В настоящее время я делаю это так (упрощенный код):

import Foundation
import Combine

struct Challenge {
  let id: Int
}

struct MyError: Error {
  let challenge: Challenge?
}

class UserManager {
  @Published var user: String?

  private func doRequest(username: String, password: String) -> AnyPublisher<String, MyError> {
    // In the real world this does a request to a REST API, returning either a User object or an error containing a Challenge.
    return Fail(outputType: String.self, failure: MyError(challenge: Challenge(id: 1)))
      .eraseToAnyPublisher()
  }

  func login(username: String, password: String, next: @escaping (Challenge) -> ()) -> AnyCancellable {
    doRequest(username: username, password: password)
      .catch { error -> AnyPublisher<String, Never> in
        if let challenge = error.challenge {
          next(challenge)
        }
        return Empty().eraseToAnyPublisher()
      }
      .sink(
        receiveValue: { [weak self] user in
          self?.user = user
        }
      )
  }
}

let userManager = UserManager()
var anyCancellables = Set<AnyCancellable>()

userManager.login(username: "hello", password: "world") { challenge in
  print("received challenge!")
}
.store(in: &anyCancellables)

Как видите, функция login принимает параметр с именем next, а издатель сам обрабатывает значение внутри оператора sink. Не очень приятно объединять издателя с таким замыканием, в идеале я бы хотел, чтобы функция login имела такую ​​подпись:

func login(username: String, password: String) -> AnyPublisher<Challenge, Never>

Но когда я хочу написать такую ​​функцию, я Я столкнулся с проблемой, когда я не могу использовать sink внутри самой функции login, так как это преобразует возвращаемый тип в AnyCancellable. По сути, я хочу превратить ошибку в вывод издателя, а также обработать фактическое значение внутри функции login. Как это возможно?

1 Ответ

0 голосов
/ 28 апреля 2020

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

...