Widget Extension 开发
问题
如何开发 iOS Widget 桌面小组件?
答案
核心概念
- TimelineProvider:提供时间线数据,告诉系统何时刷新
- TimelineEntry:一个时间点的数据快照
- View:SwiftUI 纯声明式 UI
基本实现
struct MyWidgetEntry: TimelineEntry {
let date: Date
let title: String
let value: String
}
struct MyWidgetProvider: TimelineProvider {
func placeholder(in context: Context) -> MyWidgetEntry {
MyWidgetEntry(date: .now, title: "Loading", value: "--")
}
func getSnapshot(in context: Context, completion: @escaping (MyWidgetEntry) -> Void) {
completion(MyWidgetEntry(date: .now, title: "步数", value: "8,234"))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<MyWidgetEntry>) -> Void) {
// 每小时刷新一次
let entry = MyWidgetEntry(date: .now, title: "步数", value: "8,234")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 1, to: .now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
struct MyWidgetView: View {
let entry: MyWidgetEntry
var body: some View {
VStack {
Text(entry.title).font(.caption)
Text(entry.value).font(.title).bold()
}
}
}
注意事项
| 限制 | 说明 |
|---|---|
| 无网络请求 | 不能在 Widget 内发请求(用 Background Task / App Group 共享数据) |
| 刷新频率 | 系统控制,每天约 40-70 次 |
| 数据共享 | 通过 App Group UserDefaults 或共享文件 |
| 交互 | iOS 17+ 支持 Button/Toggle(AppIntent) |
常见面试问题
Q1: Widget 和主 App 如何共享数据?
答案:通过 App Group。在 Capability 中添加 App Group,然后使用 UserDefaults(suiteName: "group.com.app") 或共享的 FileManager container 读写数据。主 App 写入数据后调用 WidgetCenter.shared.reloadAllTimelines() 通知 Widget 刷新。