我正在尝试使用 combine 将两个请求链接在一起。代码很粗糙,但是我需要调用两个api请求。一种用于获取计划数据,一种用于获取实时数据。我能够获得实时数据(第二次请求),但是我如何获得日程数据(第一次请求)?我很难理解如何使用 combine 将两个请求链接在一起,这是我第一次需要将 combine 用于我正在处理的小部件。我对 Swift 还很陌生,所以我的术语可能缺乏。
我的最后一个代码示例不正确,我的问题不清楚。我有两个publishers
,第二个取决于第一个。我对如何处理来自我的第一个 publisher
以及第二个数据的 .flatMap
中的数据的理解仍然不清楚。它是否需要是 ObservableObject
类并为数据提供 @Published
变量?我是否使用 .assign
或 .sink
从我的可编码数据 Schedule
和 Live
中获取数据?文章创建自定义 extensions
并将 API 数据更改为 nested types
时对我来说似乎有点太先进了。
新的示例代码
import Foundation
import Combine
class DataGroup {
// How to get data from Schedule and Live codable data, do I use a variable and .assign or .sink?
// Where do I put the subscriber?
func requestSchedule(_ teamID : Int) -> AnyPublisher<Schedule, Error> {
let url = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule?teamId=\(teamID)")!
return URLSession
.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Schedule.self, decoder: JSONDecoder())
.flatMap {self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")}
/*
.flatMap {URLSession.shared.dataTaskPublisher(for: URL(string: $0.dates.first?.games.first?.link ?? "")!)}
*/
.eraseToAnyPublisher()
}
// Remove and put into flatMap URLSession.shared.dataTaskPublisher?
func fetchLiveFeed(_ link: String) -> AnyPublisher<Live, Error> {
let url = URL(string: "https://statsapi.web.nhl.com\(link)")!
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Live.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
旧
import Foundation
import Combine
class CombineData {
var schedule: Schedule? // Get schedule data alongside live data
var live: Live?
private var cancellables = Set<AnyCancellable>()
func fetchSchedule(_ teamID: Int, _ completion: @escaping (/* Schedule, */Live) -> Void) {
let url = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule?teamId=\(teamID)")!
URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: Schedule.self, decoder: JSONDecoder())
.flatMap { self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "") }
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in }) { data in
// How to get both schedule data and live data here?
//self.schedule = ?
self.live = data
print(data)
completion(self.schedule!, self.live!)
}.store(in: &cancellables)
}
func fetchLiveFeed(_ link: String) -> AnyPublisher<Live, Error> {
let url = URL(string: "https://statsapi.web.nhl.com\(link)")!
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Live.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
罢工>
答案 0 :(得分:0)
一般的想法是使用 flatMap
进行链接,这就是您所做的,但是如果您还需要原始值,则可以返回一个 Zip
发布者(带有 {{1} } operator) 将两个结果放入一个元组中。
其中一个发布者是第二个请求,另一个应该只发出值。您通常可以使用 .zip
执行此操作,但您必须确保其失败类型(Just(v)
)与其他发布者匹配。您可以将其失败类型与 Never
匹配:
.setFailureType(to:)
或者,您可以使用 publisher1
.flatMap { one in
Just(one).setFailureType(to: Error.self) // error has to match publisher2
.zip(publisher2(with: one))
}
.sink(receiveCompletion: { completion in
// ...
}, receiveValue: { (one, two) in
// ...
})
来推断错误(但可能看起来有些奇怪):
Result.Publisher
所以,在你的情况下,它将是这样的:
.flatMap { one in
Result.Publisher(.success(one))
.zip(publisher2)
}