如何刷新 Widget 数据? [英] How to refresh Widget data?

查看:14
本文介绍了如何刷新 Widget 数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的代码获取两个 JSON 变量并应在我的小部件上显示它们.小部件保持空白.如果没有小部件,它会显示我的应用程序中的所有内容.

My Code fetches two JSON variables and should show them on my Widget. The Widget stays blank. Without widget it shows me everything correct in my application.

我做错了什么?代码中的 API 仅用于测试,因此您也可以检查.我需要更改什么以使其显示在小部件中吗?

What am I doing wrong? The API in the code is only for testing so you can also check that. Is there something I need to change to make it show in the widget?

我的结构:

import Foundation

struct Results: Decodable {
    let data: [Post]
}

struct Post: Decodable, Identifiable {
    let id: String
    var objectID: String {
        return id
    }
    let home_name: String
    let away_name: String
}

获取 JSON:

import Foundation

class NetworkManager: ObservableObject {
    
    @Published var posts = [Post]()
    
    @Published var test = ""
    @Published var test2 = ""
    
    func fetchData() {
        if let url = URL(string: "https://livescore-api.com/api-client/teams/matches.json?number=10&team_id=19&key=I2zBIRH3S01Kf0At&secret=6kLvfRivnqeNKUzsW84F0LISMJC1KdvQ&number=7&team_id=46") {
            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.posts = results.data
                                self.test = results.data[0].away_name
                                self.test2 = results.data[0].home_name
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

显示小部件:

import WidgetKit
import SwiftUI
import Intents

struct Provider: IntentTimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), configuration: ConfigurationIntent())
    }

    func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), configuration: configuration)
        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()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate, configuration: configuration)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: ConfigurationIntent
}

struct WidgetNeuEntryView : View {
    
    @ObservedObject var networkManager = NetworkManager()
    var entry: Provider.Entry
    var body: some View {
        Text(networkManager.test)
    }
}

@main
struct WidgetNeu: Widget {
    let kind: String = "WidgetNeu"

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
            WidgetNeuEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

struct WidgetNeu_Previews: PreviewProvider {
    static var previews: some View {
        WidgetNeuEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

networkManager.test 应该显示为文本,但正如我所说,它是空白的.

networkManager.test should be shown as text but as I said it is blank.

推荐答案

你不能像在你的应用程序中那样使用 ObservedObject.

You can't use the ObservedObject like you'd normally use in your App.

在小部件中,您使用 TimelineProvider 为您的视图创建 Entry.

In Widgets you use a TimelineProvider which creates an Entry for your view.

  1. 为你的TimelineEntry添加另一个属性,我们称之为clubName:
  1. Add another property to your TimelineEntry, let's call it clubName:

struct SimpleEntry: TimelineEntry {
    let date: Date
    let clubName: String
}

  1. 更新NetworkManager并在completion中返回结果:
  1. Update the NetworkManager and return results in the completion:

class NetworkManager {
    func fetchData(completion: @escaping ([Post]) -> Void) {
        ...
        URLSession(configuration: .default).dataTask(with: url) { data, _, error in
            ...
            let result = try JSONDecoder().decode(Results.self, from: data)
            completion(result.data)
            ...
        }
        .resume()
    }
}

  1. 使用 TimelineProvider 中的 NetworkManager 并在 fetchData 完成时创建时间线条目:
  1. Use the NetworkManager in the TimelineProvider and create timelines entries when the fetchData completes:

struct Provider: TimelineProvider {
    var networkManager = NetworkManager()

    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), clubName: "Club name")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), clubName: "Club name")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        networkManager.fetchData { posts in
            let entries = [
                SimpleEntry(date: Date(), clubName: posts[0].home_name)
            ]
            let timeline = Timeline(entries: entries, policy: .never)
            completion(timeline)
        }
    }
}

  1. 在视图主体中使用 entry.clubName:

struct WidgetNeuEntryView: View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text(entry.date, style: .time)
            Text("Club: (entry.clubName)")
        }
    }
}


请注意,在上面的示例中,重新加载策略设置为 never 以仅加载数据一次.

如果您想自动重新加载时间线,您可以轻松地将其更改为 atEndafter(date:).

You can easily change it to atEnd or after(date:) if you want to reload the timeline automatically.

如果您需要在任何时候手动重新加载时间线,您只需调用:

If you need to reload the timeline manually at any point you can just call:

WidgetCenter.shared.reloadAllTimelines()

这在 App 和 Widget 中都可以使用.

This will work in both App and Widget.

这是一个 GitHub 存储库,其中包含不同的小部件示例,包括网络小部件.

Here is a GitHub repository with different Widget examples including the Network Widget.

这篇关于如何刷新 Widget 数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆