Combine:链式请求与依赖,保留两个响应
Combine: Chain requests with dependency, keep both responses
我正在努力思考 Combine 中的这个调用。
我有两个型号和电话。一个是位置数据数组,第二个是 OpenWeather 响应的数组。我需要的是将第一次呼叫响应中的纬度和经度传递到第二次呼叫中。同时我需要保留两个调用的响应。
请记住,这是我的第一个链接请求。
enum callAPI {
static let agent = Agent()
static let url1 = URL(string: "**jsonURL**")!
static let url2 = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)&APPID=**APIkey**&unites=metric")! }
extension callAPI {
static func places() -> AnyPublisher<[Place], Error> {
return run(URLRequest(url: url1))
}
static func weather(latitude: Double, longitude: Double) -> AnyPublisher<[ResponseBody], Error> {
return run(URLRequest(url: url2))
}
static func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<T, Error> {
return agent.run(request)
.map(\.value)
.eraseToAnyPublisher()
}}
func chain() {
let places = callAPI.places()
let firstPlace = places.compactMap { [=10=].first }
let weather = firstPlace.flatMap { place in
callAPI.weather(latitude: place.latitude, longitude: place.longitude)
}
let token = weather.sink(receiveCompletion: { _ in },
receiveValue: { print([=10=]) })
RunLoop.main.run(until: Date(timeIntervalSinceNow: 10))
withExtendedLifetime(token, {})}
这是型号:
struct Place: Decodable, Identifiable {
let id: Int
let name: String
let description: String
let latitude, longitude: Double
let imageURL: String }
struct ResponseBody: Decodable {
var coord: CoordinatesResponse
var weather: [WeatherResponse]
var main: MainResponse
var name: String
var wind: WindResponse
struct CoordinatesResponse: Decodable {
var lon: Double
var lat: Double
}
struct WeatherResponse: Decodable {
var id: Double
var main: String
var description: String
var icon: String
}
struct MainResponse: Decodable {
var temp: Double
var feels_like: Double
var temp_min: Double
var temp_max: Double
var pressure: Double
var humidity: Double
}
struct WindResponse: Decodable {
var speed: Double
var deg: Double
}}
extension ResponseBody.MainResponse {
var feelsLike: Double { return feels_like }
var tempMin: Double { return temp_min }
var tempMax: Double { return temp_max }}
您将无法按照您尝试的方式链接请求并仍然捕获所有结果。
这样想。通过链接 Combine 运算符,您正在构建一个管道。您可以决定将什么放入管道的输入中,也可以将管道输出的任何内容转储到 sink
中,您可以在其中看到结果,但不能通过管道以查看中间值(至少在我们将要到达的管道中切割 window 时不会)。
这是您的代码:
let places = callAPI.places()
let firstPlace = places.compactMap { [=10=].first }
let weather = firstPlace.flatMap { place in
callAPI.weather(latitude: place.latitude, longitude: place.longitude)
}
let token = weather.sink(receiveCompletion: { _ in },
receiveValue: { print([=10=]) })
这些变量每个都有一部分管道(不是将流经管道的值),您正在将管道拧在一起,在每个变量中放置越来越长的部分。
如果我想让整个管道更明显一点,可以这样写:
let cancellable = callAPI.places()
.compactMap { [=11=].first }
.flatMap { place in
callAPI.weather(latitude: place.latitude, longitude: place.longitude)
}
.sink(receiveCompletion: { _ in },
receiveValue: { print([=11=]) })
(请注意,可能无法按原样编译...我在答案编辑器中将其整合在一起)
当您直接链接运算符时,很明显您没有任何机会捕获中间结果。对于您的管道,进入管道的内容来自网络。您在 sink
中捕捉到流出管道的东西。但是请注意,您如何只能在作为管道本身一部分的闭包中查看流经管道的“东西”。
现在,如果您真的想将 window 切入管道以提取中间内容,您需要其中一个可以将值推出管道的闭包。在这种情况下,要获取 Places
的数组,您可以使用 handleEvents
来完成。它看起来像这样:
var allPlaces : [Place]?
callAPI.places()
.handleEvents(receiveOutput: { allPlaces = [=12=] })
.compactMap { [=12=].first }
...
在此代码中,您捕获了 receiveOutput
事件并将结果偷偷放入附近的变量中。
handleEvents
,在我看来,就是那些“实力大,责任大”的运营商之一。在这种情况下,它会让你做你要求做的事,但我不确定你是否应该这样做。
将运算符链接在一起的全部意义在于生成的管道“应该”没有 side-effects。在这种情况下,handleEvents
用于显式引入 side-effect(设置 allPlaces
变量)。从本质上讲,在我看来,这是一种代码味道,表明您可能需要重新考虑您的设计。
我正在努力思考 Combine 中的这个调用。
我有两个型号和电话。一个是位置数据数组,第二个是 OpenWeather 响应的数组。我需要的是将第一次呼叫响应中的纬度和经度传递到第二次呼叫中。同时我需要保留两个调用的响应。
请记住,这是我的第一个链接请求。
enum callAPI {
static let agent = Agent()
static let url1 = URL(string: "**jsonURL**")!
static let url2 = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)&APPID=**APIkey**&unites=metric")! }
extension callAPI {
static func places() -> AnyPublisher<[Place], Error> {
return run(URLRequest(url: url1))
}
static func weather(latitude: Double, longitude: Double) -> AnyPublisher<[ResponseBody], Error> {
return run(URLRequest(url: url2))
}
static func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<T, Error> {
return agent.run(request)
.map(\.value)
.eraseToAnyPublisher()
}}
func chain() {
let places = callAPI.places()
let firstPlace = places.compactMap { [=10=].first }
let weather = firstPlace.flatMap { place in
callAPI.weather(latitude: place.latitude, longitude: place.longitude)
}
let token = weather.sink(receiveCompletion: { _ in },
receiveValue: { print([=10=]) })
RunLoop.main.run(until: Date(timeIntervalSinceNow: 10))
withExtendedLifetime(token, {})}
这是型号:
struct Place: Decodable, Identifiable {
let id: Int
let name: String
let description: String
let latitude, longitude: Double
let imageURL: String }
struct ResponseBody: Decodable {
var coord: CoordinatesResponse
var weather: [WeatherResponse]
var main: MainResponse
var name: String
var wind: WindResponse
struct CoordinatesResponse: Decodable {
var lon: Double
var lat: Double
}
struct WeatherResponse: Decodable {
var id: Double
var main: String
var description: String
var icon: String
}
struct MainResponse: Decodable {
var temp: Double
var feels_like: Double
var temp_min: Double
var temp_max: Double
var pressure: Double
var humidity: Double
}
struct WindResponse: Decodable {
var speed: Double
var deg: Double
}}
extension ResponseBody.MainResponse {
var feelsLike: Double { return feels_like }
var tempMin: Double { return temp_min }
var tempMax: Double { return temp_max }}
您将无法按照您尝试的方式链接请求并仍然捕获所有结果。
这样想。通过链接 Combine 运算符,您正在构建一个管道。您可以决定将什么放入管道的输入中,也可以将管道输出的任何内容转储到 sink
中,您可以在其中看到结果,但不能通过管道以查看中间值(至少在我们将要到达的管道中切割 window 时不会)。
这是您的代码:
let places = callAPI.places()
let firstPlace = places.compactMap { [=10=].first }
let weather = firstPlace.flatMap { place in
callAPI.weather(latitude: place.latitude, longitude: place.longitude)
}
let token = weather.sink(receiveCompletion: { _ in },
receiveValue: { print([=10=]) })
这些变量每个都有一部分管道(不是将流经管道的值),您正在将管道拧在一起,在每个变量中放置越来越长的部分。
如果我想让整个管道更明显一点,可以这样写:
let cancellable = callAPI.places()
.compactMap { [=11=].first }
.flatMap { place in
callAPI.weather(latitude: place.latitude, longitude: place.longitude)
}
.sink(receiveCompletion: { _ in },
receiveValue: { print([=11=]) })
(请注意,可能无法按原样编译...我在答案编辑器中将其整合在一起)
当您直接链接运算符时,很明显您没有任何机会捕获中间结果。对于您的管道,进入管道的内容来自网络。您在 sink
中捕捉到流出管道的东西。但是请注意,您如何只能在作为管道本身一部分的闭包中查看流经管道的“东西”。
现在,如果您真的想将 window 切入管道以提取中间内容,您需要其中一个可以将值推出管道的闭包。在这种情况下,要获取 Places
的数组,您可以使用 handleEvents
来完成。它看起来像这样:
var allPlaces : [Place]?
callAPI.places()
.handleEvents(receiveOutput: { allPlaces = [=12=] })
.compactMap { [=12=].first }
...
在此代码中,您捕获了 receiveOutput
事件并将结果偷偷放入附近的变量中。
handleEvents
,在我看来,就是那些“实力大,责任大”的运营商之一。在这种情况下,它会让你做你要求做的事,但我不确定你是否应该这样做。
将运算符链接在一起的全部意义在于生成的管道“应该”没有 side-effects。在这种情况下,handleEvents
用于显式引入 side-effect(设置 allPlaces
变量)。从本质上讲,在我看来,这是一种代码味道,表明您可能需要重新考虑您的设计。