如何在同一链中的 dataTaskPublisher 请求中使用来自一个发布者的值?
How do I use the value from one publisher in a request for a dataTaskPublisher in the same chain?
我是合并的新手,正在尝试弄清楚如何链接 Publishers。我有一个发布者 returns 一个字符串值,我想用它来构建 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 { [=10=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
}
.eraseToAnyPublisher() /* Error: Type of expression is ambiguous without more context */
}
注意:我很不确定最后一个eraseToAnyPublisher
的位置
你有两个问题。
首先,您将 flatMap
的形式参数命名为 valueKey
,但后来您尝试使用名称 value
来引用它。这些需要匹配。
其次,Swift 无法像您传递给 flatMap
的闭包那样推断出 multi-statement 函数的 return 类型。 (它 可以 将参数类型推断为发布者 return 由 getValueKey
编辑的 Output
类型。)
有两种一般方法可以解决此问题:
- 更改您的代码,使闭包仅包含一个语句,或
- 明确给出 return 类型。
将其重写为单个语句的一种方法是使用 map
运算符将传入的 String
转换为 URLRequest
,如下所示:
func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
return getValueKey()
.map { URLRequest(url: URL(string: "http://www.test.com/\([=10=])")!) }
.flatMap {
return URLSession.shared.dataTaskPublisher(for: [=10=])
.map { [=10=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
}
.eraseToAnyPublisher()
}
我们还可以通过使用 mapError
将 URLError
转换为 Error
然后将其他运算符移到 flatMap
之外来减少嵌套:
func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
return getValueKey()
.map { URLRequest(url: URL(string: "http://www.test.com/\([=11=])")!) }
.flatMap { URLSession.shared.dataTaskPublisher(for: [=11=]).mapError { [=11=] as Error } }
.map { [=11=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
我提到的另一种方法是使闭包的 return 类型显式化。这很棘手,因为 return 类型很复杂。您需要向右滚动才能看到全部内容:
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 { [=12=].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 { [=13=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
.eraseToAnyPublisher() /* Error: Type of expression is ambiguous without more context */
}
我是合并的新手,正在尝试弄清楚如何链接 Publishers。我有一个发布者 returns 一个字符串值,我想用它来构建 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 { [=10=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
}
.eraseToAnyPublisher() /* Error: Type of expression is ambiguous without more context */
}
注意:我很不确定最后一个eraseToAnyPublisher
你有两个问题。
首先,您将 flatMap
的形式参数命名为 valueKey
,但后来您尝试使用名称 value
来引用它。这些需要匹配。
其次,Swift 无法像您传递给 flatMap
的闭包那样推断出 multi-statement 函数的 return 类型。 (它 可以 将参数类型推断为发布者 return 由 getValueKey
编辑的 Output
类型。)
有两种一般方法可以解决此问题:
- 更改您的代码,使闭包仅包含一个语句,或
- 明确给出 return 类型。
将其重写为单个语句的一种方法是使用 map
运算符将传入的 String
转换为 URLRequest
,如下所示:
func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
return getValueKey()
.map { URLRequest(url: URL(string: "http://www.test.com/\([=10=])")!) }
.flatMap {
return URLSession.shared.dataTaskPublisher(for: [=10=])
.map { [=10=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
}
.eraseToAnyPublisher()
}
我们还可以通过使用 mapError
将 URLError
转换为 Error
然后将其他运算符移到 flatMap
之外来减少嵌套:
func performSearch(_ searchTerm: String) -> AnyPublisher<[ResultObject], Error> {
return getValueKey()
.map { URLRequest(url: URL(string: "http://www.test.com/\([=11=])")!) }
.flatMap { URLSession.shared.dataTaskPublisher(for: [=11=]).mapError { [=11=] as Error } }
.map { [=11=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
我提到的另一种方法是使闭包的 return 类型显式化。这很棘手,因为 return 类型很复杂。您需要向右滚动才能看到全部内容:
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 { [=12=].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 { [=13=].data }
.decode(type: [ResultObject].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
.eraseToAnyPublisher() /* Error: Type of expression is ambiguous without more context */
}