Swift 重新加载Widgetcenter的所有时间线
我在我的主应用程序中使用Swift 重新加载Widgetcenter的所有时间线,swift,swiftui,widgetkit,Swift,Swiftui,Widgetkit,我在我的主应用程序中使用WidgetCenter.shared.reloadAllTimelines()来刷新我的小部件 该小部件包含一个图片和一个字符串,该字符串通过json请求获取。 如果我使用上面的代码,图片会立即刷新。这就是它应该是什么样子 但是字符串保持不变。它需要执行另一个json请求。但事实并非如此。它显示了旧字符串。为什么? 使用TimelineEntry导入字符串。所以我想我也需要重新加载TimelineEntry 我该怎么做?对于我在视图中使用的字符串entry.clubna
WidgetCenter.shared.reloadAllTimelines()
来刷新我的小部件
该小部件包含一个图片和一个字符串,该字符串通过json请求获取。
如果我使用上面的代码,图片会立即刷新。这就是它应该是什么样子
但是字符串保持不变。它需要执行另一个json请求。但事实并非如此。它显示了旧字符串。为什么?
使用TimelineEntry导入字符串。所以我想我也需要重新加载TimelineEntry
我该怎么做?对于我在视图中使用的字符串entry.clubname
这是一些示例代码。我删除了一点,这样代码就不多了
我的网络:
class NetworkManager: ObservableObject {
@Published var clubNameHome = "..."
init() {
fetchData() // fetch data must be called at least once
}
func fetchData() {
if let url = URL(string: "...) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (gettingInfo, response, error) in
if error == nil {
let decoder = JSONDecoder()
if let safeData = gettingInfo {
do {
let results = try decoder.decode(Results.self, from: safeData)
DispatchQueue.main.async {
self.clubNameHome = results.data[0]....
if #available(iOS 14.0, *) {
WidgetCenter.shared.reloadAllTimelines()
} else {
// Fallback on earlier versions
}
}
} catch {
print(error)
}
}
}
}
task.resume()
}
}
}
以及我的TimelineProvider和View:
struct Provider: IntentTimelineProvider {
let networkManager = NetworkManager()
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationIntent(), clubnamehome: networkManager.clubNameHome)
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration, clubnamehome: networkManager.clubNameHome)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)!
let entry = SimpleEntry(date: entryDate, configuration: configuration, clubnamehome: networkManager.clubNameHome)
entries.append(entry)
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
let clubnamehome: String
}
struct MyTeamWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
HStack {
Spacer()
VStack (alignment: .leading, spacing: 0) {
Spacer().frame(height: 10)
HStack {
Spacer()
switch logo {
case "arminia":
Image("bundesliga1/arminia").resizable().aspectRatio(contentMode: .fit).frame(width: 90, height: 90, alignment: .center)
case "augsburg":
Image("bundesliga1/augsburg").resizable().aspectRatio(contentMode: .fit).frame(width: 90, height: 90, alignment: .center)
default:
Image("bundesliga1/bayern").resizable().aspectRatio(contentMode: .fit).frame(width: 90, height: 90, alignment: .center)
}
}
HStack{
Text(entry.clubname)
}
}
}}}
struct Provider:IntentTimelineProvider{
让networkManager=networkManager()
func占位符(在上下文中:context)->SimpleEntry{
SimpleEntry(日期:date(),配置:ConfigurationContent(),clubnamehome:networkManager.clubnamehome)
}
func getSnapshot(对于配置:configurationcontent,在上下文:context中,完成:@escaping(SimpleEntry)->()){
let entry=SimpleEntry(日期:date(),配置:configuration,clubnamehome:networkManager.clubnamehome)
完成(进入)
}
func getTimeline(对于配置:configurationcontent,在上下文:context中,完成:@escaping(Timeline)->()){
变量项:[SimpleEntry]=[]
//从当前日期开始,每隔一小时生成由五个条目组成的时间线。
让currentDate=Date()
让entryDate=Calendar.current.date(通过添加:.minute,值:15,到:currentDate)!
let entry=SimpleEntry(日期:entryDate,配置:configuration,clubnamehome:networkManager.clubnamehome)
条目。追加(条目)
let timeline=timeline(条目:条目,策略:.atEnd)
完成(时间表)
}
}
结构SimpleEntry:TimelineEntry{
日期:日期
let配置:配置内容
让clubnamehome:字符串
}
结构MyTeamWidgetEntryView:视图{
var条目:Provider.entry
var body:一些观点{
HStack{
垫片()
VStack(对齐:。前导,间距:0){
隔套().框架(高度:10)
HStack{
垫片()
开关标志{
“阿米尼亚”案:
图像(“德甲联赛/arminia”).resizable().aspectRatio(内容模式:.fit).边框(宽度:90,高度:90,对齐方式:.center)
“奥格斯堡”案:
图像(“德甲联赛/奥格斯堡”).resizable().aspectRatio(内容模式:.fit).边框(宽度:90,高度:90,对齐方式:.center)
违约:
图像(“德甲/拜仁”).resizable().aspectRatio(contentMode:.fit).边框(宽度:90,高度:90,对齐方式:.center)
}
}
HStack{
文本(entry.clubname)
}
}
}}}
问题是您的网络呼叫应该位于getTimeline(for:in:completion:)->Void)
。它将在WidgetViewModel类的完成处理程序中执行数据。
getTimeline方法将每15分钟自动触发一次,如代码所示
所以它一定是这样的,但是请注意,您必须根据您的项目更改代码。因为它只用于widget,我建议您使用Combine并为您的widget创建这个网络调用
因此,我将用以下方式重写您的NetworkManager:
import Combine
class NetworkManager<Resource> where Resource: Codable {
func fetchData() -> AnyPublisher<Resource, Error> {
URLSession.shared
.dataTaskPublisher(for: URL(string: "....")!) // Don't forget your url
.receive(on: DispatchQueue.main)
.map(\.data)
.decode(
type: Resource.self,
decoder: JSONDecoder())
.mapError { error -> Error in
return error }
.eraseToAnyPublisher()
}
}
谢谢你的建议。在我的网络通话中,我到底需要重写什么?我没有太多的经验与完成处理。我试过上面的代码,它说:传递给调用的参数不需要arguments@submariner我已经为您做了一些更改,以便您能够遵循流程。问题是在getTimeline函数中调用NetworkManager。您还可以在getSnapshot(in:completion:)中使用此viewModel.downloadWidgetData来显示小部件库中的数据。如果你有任何问题,让我知道,我们会解决的。谢谢你的回答。这看起来比我想的更复杂。有可能以某种方式与您联系吗?我甚至愿意付钱。我真的无法解决这个问题,因为我对swift太陌生了。这是显示的我的真实姓名,所以如果您愿意,请随时通过LinkedIn、Twitter或Messenger与我联系,我可以帮助您编写源代码。但是你应该一步一步地尝试我的答案,并按照这个链接的解释进行操作
import Combine
final class WidgetViewModel {
private var subscriptions = Set<AnyCancellable>()
// You can see here how to use a completion handler that is set to be
// a String, the same as the value your expect for 'clubnamehome'.
func downloadWidgetData(completionHandlers: @escaping (String) -> Void) {
NetworkManager<String>().fetchData()
.sink(
receiveCompletion: { _ in },
receiveValue: { data in
completionHandlers(data) }) // Completion Handler set here
.store(in: &subscriptions)
}
}
let viewModel = WidgetViewModel()
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
let currentDate = Date()
guard let refreshTimeline = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate) else { return }
// You must declare your network call here and handle the data as a completionHandler.
// Like this, the widget will refresh every 15 minutes.
viewModel.downloadWidgetData { data in // CompletionHandler is used like this.
let entry = SimpleEntry(date: currentDate, configuration: configuration, clubnamehome: data)
let timeline = Timeline(entries: [entry], policy: .after(refreshTimeline))
completion(timeline)
}
}