如何在同一链中的dataTaskPublisher请求中使用一个发布者的值?

时间:2020-11-02 00:37:01

标签: swift combine

我是新来的要合并的人,并试图弄清楚如何链接发布者。我有一个发布者,它返回一个我想用来构建URLRequest的字符串值,然后将其传递给DataTaskPublisher。使用正确语法的任何帮助将不胜感激!

示例代码:

struct ResultObject: Decodable {}

func getValueKey() -> AnyPublisher<String, Error> {
    return Just("Test")
        .setFailureType(to: Error.self)
        .eraseToAnyPublisher()
}

func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
    return getValueKey().flatMap { valueKey in
        let request: URLRequest = URLRequest(url: URL(string: "http://www.test.com/\(valueKey)")!)
        return URLSession.shared.dataTaskPublisher(for: request)
            .map { $0.data }
            .decode(type: [ResultObject].self, decoder: JSONDecoder())
    }
    .eraseToAnyPublisher() /* Error: Type of expression is ambiguous without more context */
}

注意:我不确定最后一个eraseToAnyPublisher

的位置

1 个答案:

答案 0 :(得分:1)

您有两个问题。

首先,您为flatMap的形式参数指定了名称valueKey,但后来尝试使用名称value来引用它。这些需要匹配。

第二,Swift无法推断出多语句函数的返回类型,例如您要传递给flatMap的闭包。 (它可以推断参数类型为Output返回的发布者的getValueKey类型。)

有两种一般方法可以解决此问题:

  • 更改代码,以便闭包仅包含一个语句,
  • 明确给出返回类型。

将其重写为单个语句的一种方法是使用map运算符将传入的String转换为URLRequest,如下所示:

func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
    return getValueKey()
        .map { URLRequest(url: URL(string: "http://www.test.com/\($0)")!) }
        .flatMap {
            return URLSession.shared.dataTaskPublisher(for: $0)
                .map { $0.data }
                .decode(type: [ResultObject].self, decoder: JSONDecoder())
        }
        .eraseToAnyPublisher()
}

我们还可以通过使用mapErrorURLError转换为Error然后将其他运算符移到flatMap之外来减少嵌套:

func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
    return getValueKey()
        .map { URLRequest(url: URL(string: "http://www.test.com/\($0)")!) }
        .flatMap { URLSession.shared.dataTaskPublisher(for: $0).mapError { $0 as Error } }
        .map { $0.data }
        .decode(type: [ResultObject].self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

我提到的另一种方法是使闭包的返回类型明确。这很棘手,因为返回类型很复杂。您需要向右滚动才能看到整个内容:

func performSearch0(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
    return getValueKey().flatMap { value -> Publishers.Decode<Publishers.Map<URLSession.DataTaskPublisher, Data>, [ResultObject], JSONDecoder> in
        let request: URLRequest = URLRequest(url: URL(string: "http://www.test.com/\(value)")!)
        return URLSession.shared.dataTaskPublisher(for: request)
            .map { $0.data }
            .decode(type: [ResultObject].self, decoder: JSONDecoder())
    }
    .eraseToAnyPublisher()
}

因此,当我们选择此解决方案时,我们通常希望使用eraseToAnyPublisher来简化类型:

func performSearch1(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
    return getValueKey().flatMap { value -> AnyPublisher<[ResultObject], Error> in
        let request: URLRequest = URLRequest(url: URL(string: "http://www.test.com/\(value)")!)
        return URLSession.shared.dataTaskPublisher(for: request)
            .map { $0.data }
            .decode(type: [ResultObject].self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
    .eraseToAnyPublisher() /* Error: Type of expression is ambiguous without more context */
}