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)
  }
}