如何刷新 Widget 数据?

Posted

技术标签:

【中文标题】如何刷新 Widget 数据?【英文标题】:How to refresh Widget data? 【发布时间】:2020-09-20 07:12:49 【问题描述】:

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

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

我的结构:

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 应该显示为文本,但正如我所说,它是空白的。

【问题讨论】:

【参考方案1】:

您不能像通常在应用中使用的那样使用ObservedObject

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


    将另一个属性添加到您的TimelineEntry,我们称之为clubName
struct SimpleEntry: TimelineEntry 
    let date: Date
    let clubName: String

    更新NetworkManager并在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()
    

    TimelineProvider 中使用NetworkManager 并在fetchData 完成时创建时间线条目:
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)
        
    

    在视图主体中使用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:)

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

WidgetCenter.shared.reloadAllTimelines()

这将在 App 和 Widget 中工作。


这是一个GitHub repository,其中包含不同的小部件示例,包括网络小部件。

【讨论】:

谢谢它的工作。我需要添加 if #available(ios 14.0, *),导入 WidgetKit 并将 entry.clubName 重命名为 entry.clubname。但是 PreviewProvider 有问题。我也需要更改它,现在它说 Instance member 'networkManager' cannot be used on type 'MyTeamWidget_Previews' 我上面解释的只是一个例子——我不知道您的财产的实际名称是什么(clubnameclubName 等)。并且 Widgets 在 iOS 14+ 中可用,所以如果您的部署目标较低,那么是的,您需要添加 if #available(iOS 14.0, *) 再次非常感谢您。但是您现在如何解决预览提供程序的问题? @submariner 注意预览是静态的——你不能在那里使用实例变量。您可以查看this answer 以获得解决方案。但实际上预览更适合创建视图布局等简单任务。最好在模拟器中测试网络代码。 您使用的是哪个 Xcode + MacOS 版本? @pawello2222

以上是关于如何刷新 Widget 数据?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 全局List Widget 不刷新问题

使用远程服务中的数据刷新简单的 Android Home Widget 的最佳方法是啥?

pyqt5怎么刷新widget

Flutter局部刷新方法

iOS14 widget 刷新

Flutter 局部Widget刷新