0

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

1 回答 1

0

问题是您的网络呼叫应该在getTimeline(for:in:completion:) -> Void). 它将在 WidgetViewModel 类的完成处理程序中执行您的数据。如您的代码所示,getTimeline 方法将每 15 分钟自动触发一次。

所以它必须寻找这样的东西,但请注意您必须根据您的项目更改代码。而且因为它仅适用于小部件,我建议您使用组合并仅为您的小部件创建此网络调用。

所以我会用这种方式重写你的 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()
  }
}

然后,我将创建一个视图模型来处理小部件逻辑并设置一个方法来处理 NetworkManager 调用以获取数据:

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

现在,在您的 中struct Provider: IntentTimelineProvider,更改您的 getTimeline 以从您的 viewModel 使用您的 NetworkManager。您可能需要对代码进行一些更改,因为我没有您的 url 来测试它和您的 Widget 的完整代码,但它应该与此设置密切合作。

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)
  }
}
于 2020-10-03T12:09:47.297 回答