我创建了一个类来执行网络请求并使用Combine
解析数据。我不完全确定代码是正确的,但到目前为止它还在运行(仍在学习Swift的基础知识和基本的网络任务(。我的小部件有正确的数据,并且一直工作到数据变为零。不知道如何检查SwiftUIView
中第一个publisher
的数据是否为零,即使没有游戏显示,数据似乎也是有效的。
我的SwiftUI视图
struct SimpleEntry: TimelineEntry {
let date: Date
public var model: CombineData?
let configuration: ConfigurationIntent
}
struct Some_WidgetEntryView : View {
var entry: Provider.Entry
@Environment(.widgetFamily) var widgetFamily
var body: some View {
VStack (spacing: 0){
if entry.model?.schedule?.dates.first?.games == nil {
Text("No games Scheduled")
} else {
Text("Game is scheduled")
}
}
}
}
联合
import Foundation
import WidgetKit
import Combine
// MARK: - Combine Attempt
class CombineData {
var schedule: Schedule?
var live: Live?
private var cancellables = Set<AnyCancellable>()
func fetchSchedule(_ teamID: Int, _ completion: @escaping (Live) -> Void) {
let url = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule?teamId=(teamID)")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.map(.data)
.decode(type: Schedule.self, decoder: JSONDecoder())
//.catch { _ in Empty<Schedule, Error>() }
//.replaceError(with: Schedule(dates: []))
let publisher2 = publisher
.flatMap {
return self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")
}
Publishers.Zip(publisher, publisher2)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {_ in
}, receiveValue: { schedule, live in
self.schedule = schedule
self.live = live
completion(self.live!)
WidgetCenter.shared.reloadTimelines(ofKind: "NHL_Widget")
}).store(in: &cancellables)
}
func fetchLiveFeed(_ link: String) -> AnyPublisher<Live, Error /*Never if .catch 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())
//.catch { _ in Empty<Live, Never>() }
.eraseToAnyPublisher()
}
}
正如我在评论中所说,decode(type: Live.self, decoder: JSONDecoder())
很可能会返回一个错误,因为当link
是nil
时,您从中获取的URL不会返回任何可以解码为Live.self
的内容。
所以你需要以某种方式处理这个案子。例如,可以通过将Live
变量设为可选变量,并在link
为空(或为零(时返回nil
来处理此问题。
这只是为了让你朝着正确的方向前进——你需要自己计算出确切的代码。
let publisher2 = publisher1
.flatMap {
self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")
.map { $0 as Live? } // convert to an optional
.replaceError(with: nil)
}
然后在sink
中,处理nil
:
.sink(receiveCompletion: {_ in }, receiveValue:
{ schedule, live in
if let live = live {
// normal treatment
self.schedule = schedule
self.live = live
//.. etc
} else {
// set a placeholder
}
})
SwiftUI
和WidgetKit
的工作方式不同。我需要在getTimeline
中为IntentTimelineProvider
获取数据,然后为TimelineEntry
添加一个completion
处理程序。严重修改了我的Combine
数据模型。所有的荣誉都归功于@EmilioPelaez为我指明了正确的方向,请在这里回答。