swift - 如何刷新Widget数据?

标签 swift swiftui widget widgetkit

我的代码获取两个 JSON变量,应该在我的 WidgetKit 上显示它们。
WidgetKit 保持空白。如果没有 WidgetKit ,它会显示我的应用程序中的所有内容。
我做错了什么?代码中的 API 仅用于测试,因此您也可以对其进行检查。
是否需要更改某些内容才能使其显示在 WidgetKit 中?
我的结构:

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

最佳答案

您不能使用 ObservedObject就像您通常在应用程序中使用的那样。
在 WidgetKit 中,您使用 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()
        }
    }
    
  • 使用 NetworkManagerTimelineProvider并在 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在 View 主体中:
  • 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使用不同的 WidgetKit 示例,包括网络 WidgetKit 。

    关于swift - 如何刷新Widget数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63976424/

    相关文章:

    ios - 向 PHImageManager 请求图像导致 iOS 8.3 中的图像错误

    swift 如何从第二个 vai 协议(protocol)和委托(delegate)模式中的逻辑更新第一个 Controller ?

    swift - 多张纸(isPresented :) doesn't work in SwiftUI

    javascript - 使用原型(prototype) JS 的 float 小部件/横幅

    php - 在 wordpress 上自定义主题我的登录小部件

    json - 使用 Alamofire 获取 json 值

    swift - DateFormatter:dateFormat 毫秒 Swift

    xcode - SwiftUI 当 View 在初始化程序中需要 @Binding 时如何实例化 PreviewProvider

    swift - VStack如何获取闭包内的View元素?

    forms - Symfony2 表单部件容器属性