【发布时间】:2021-10-14 09:26:05
【问题描述】:
简要说明;我正在为我的特斯拉汽车做一个项目。 Tesla 已经有一个只能添加到“今日视图”选项卡的小部件,当我滑动到“今日视图”时,该小部件会自动刷新。这是它的样子:Image here
我想在我的小部件中完成同样的事情。我基本上有工作代码,请看下面:
注意:我的这个项目仅供实验和个人使用。
extension Date {
func timeAgoDisplay() -> String {
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .full
return formatter.localizedString(for: self, relativeTo: Date())
}
}
import WidgetKit
import SwiftUI
import Intents
import TeslaSwift
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)
}
getVehicle() // Run this function to get vehicle info
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
}
var lastUpdated = String()
var batteryLevel = Int()
var interiorTemperature = Double()
func getVehicle() {
let apii = TeslaSwift()
if let jsonString = UserDefaults(suiteName: "group.widget")!.string(forKey: "GlobalToken"),
let token: AuthToken = jsonString.decodeJSON(),
let _ = UserDefaults(suiteName: "group.widget")!.string(forKey: "GlobalToken") {
apii.reuse(token: token, email: nil)
}
apii.useMockServer = false
apii.debuggingEnabled = true
let id = UserDefaults(suiteName: "group.widget")!.string(forKey: "GlobalSelectedID")
apii.getVehicle(id!).done {
(vehicle: Vehicle) -> Void in
apii.getAllData(vehicle).done { (extendedVehicle: VehicleExtended) in
batteryLevel = (extendedVehicle.chargeState?.batteryLevel)!
interiorTemperature = (extendedVehicle.climateState?.insideTemperature!.celsius)!
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy - HH:mm:ss"
let now = Date()
let dateString = formatter.string(from:now)
lastUpdated = dateString
}.catch { (error) in
print("error1: \(error)")
}
}.catch { error in
print("error2: \(error)")
}
}
struct PWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
Text("Battery: \(batteryLevel)%")
Text("Temparature: \(String(format: "%.0f", interiorTemperature))")
Text("Last Updated: \(lastUpdated)")
.environment(\.sizeCategory, .extraSmall)
}
}
}
@main
struct PWidget: Widget {
let kind: String = "PWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
PWidgetEntryView(entry: entry)
}
.supportedFamilies([.systemMedium])
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct PWidget_Previews: PreviewProvider {
static var previews: some View {
PWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}
所以现在,当使用此代码时。它完美地获取数据,但小部件不显示数据。
如果我在lastUpdated = dateString 之后添加WidgetCenter.shared.reloadAllTimelines(),小部件会更新,但也会保持大约每五秒更新一次。这会消耗大量电池。
我还尝试在 func getVehicle() { 之外添加 var didUpdateManually = false,然后像这样进行 if false 检查。这使得它更新了一次小部件,但永远不会再更新:
if (didUpdateManually == false) {
WidgetCenter.shared.reloadAllTimelines()
didUpdateManually = true
}
所以基本上我要完成两/三件事:
1. Display the value from API to my widget (batteryLevel, interiorTemperature and lastUpdated timestamp).
2. If either or both is possible:
2.A: When the widget is added to the Today View tab, I want to automatically update the widget by re-running the `func getVehicle()` and update the info when the user swipe to the Today View tab.
2.B: If the widget is on the home screen page, I want to widget to automatically update when the in the same way as 2A, or update once every hour or so.
【问题讨论】:
-
这不是小部件的工作方式。您的小部件配置为每小时更新一次,持续 5 小时,但您不向其传递任何数据 — SimpleEntry 应包含小部件需要更新的所有数据。如果您还没有尝试过 Apple 的 WidgetKit 教程,那么这是了解所有这些组件如何协同工作的好方法:developer.apple.com/news/?id=yv6so7ie
-
@Adam 是的,看起来我有点误解了。谢谢你的解释。你说
Your widget is configured to update once per hour for five hours这到底是什么意思?我的 Widget 会在 5 小时内每小时更新一次,然后就再也不会更新了? -
您的代码创建了一个包含 5 个条目的时间线和“.atEnd”刷新策略。因此,您的小部件将使用这 5 个条目更新 5 个小时,然后在第五个条目之后的“某个时间”,系统将再次调用您的 getTimeline() 方法。我说“有时”是因为系统控制小部件更新的频率;你只能告诉它你想要发生的事情。更多细节在这里:developer.apple.com/documentation/widgetkit/…
-
@Adam 好的,因为我的小部件显示的日期和时间在每次小部件更新时都应该不同。在这种情况下,我现在使用的方法是否正确?
-
这是一个旧的“今日小部件”,它仍然可以工作,但在 iOS 14 中已被弃用。它们每次出现时都可以运行代码,但新的小部件不能。