如何刷新 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
以仅加载一次数据。
如果您想自动重新加载时间线,您可以轻松地将其更改为 atEnd
或 after(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' 我上面解释的只是一个例子——我不知道您的财产的实际名称是什么(clubname
、clubName
等)。并且 Widgets 在 iOS 14+ 中可用,所以如果您的部署目标较低,那么是的,您需要添加 if #available(iOS 14.0, *)
。
再次非常感谢您。但是您现在如何解决预览提供程序的问题?
@submariner 注意预览是静态的——你不能在那里使用实例变量。您可以查看this answer 以获得解决方案。但实际上预览更适合创建视图布局等简单任务。最好在模拟器中测试网络代码。
您使用的是哪个 Xcode + MacOS 版本? @pawello2222以上是关于如何刷新 Widget 数据?的主要内容,如果未能解决你的问题,请参考以下文章