如何使用SwiftData配置App Widget:基于TaskItem的实现示例
基于SwiftData实现Widget任务选择配置示例
核心需求
通过SwiftData的TaskItem对象配置Widget,实现用户选择任务后,Widget展示所选任务的信息,核心是实现符合AppEntity规范的AppIntent。
完整实现代码
1. SwiftData模型(TaskItem)
定义SwiftData的TaskItem模型,同时遵循AppEntity、Equatable、Hashable协议,满足Widget配置的实体选择要求:
import SwiftData import AppIntents @Model class TaskItem: Identifiable, Equatable, Hashable, AppEntity { // SwiftData存储属性 var id: UUID var title: String var content: String var isCompleted: Bool // AppEntity协议配置 static var typeDisplayRepresentation: TypeDisplayRepresentation = "任务列表" static var typeDisplayName: LocalizedStringResource = "任务" // 指定默认查询类 typealias DefaultQueryType = TaskItemQuery static var defaultQuery: TaskItemQuery = TaskItemQuery() // 实体在配置界面的展示信息 var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: .init(stringLiteral: title), subtitle: .init(stringLiteral: isCompleted ? "已完成" : "未完成") ) } // 初始化方法 init(id: UUID = UUID(), title: String, content: String, isCompleted: Bool = false) { self.id = id self.title = title self.content = content self.isCompleted = isCompleted } // Equatable协议实现 static func == (lhs: TaskItem, rhs: TaskItem) -> Bool { lhs.id == rhs.id } // Hashable协议实现 func hash(into hasher: inout Hasher) { hasher.combine(id) } }
2. SwiftData查询类(TaskItemQuery)
实现EntityStringQuery协议,用于在Widget配置界面搜索任务、根据ID获取任务:
import SwiftData import AppIntents struct TaskItemQuery: EntityStringQuery { typealias Entity = TaskItem // 根据关键词搜索匹配任务 func entities(matching string: String) async throws -> [TaskItem] { let container = try ModelContainer(for: TaskItem.self) let descriptor = FetchDescriptor<TaskItem>( predicate: #Predicate<TaskItem> { $0.title.localizedStandardContains(string) || $0.content.localizedStandardContains(string) }, sortBy: [SortDescriptor(\TaskItem.title)] ) return try container.mainContext.fetch(descriptor) } // 根据ID数组获取对应任务 func entities(for identifiers: [UUID]) async throws -> [TaskItem] { let container = try ModelContainer(for: TaskItem.self) let descriptor = FetchDescriptor<TaskItem>( predicate: #Predicate<TaskItem> { identifiers.contains($0.id) } ) return try container.mainContext.fetch(descriptor) } }
3. Widget配置Intent(SelectTaskIntent)
定义用于Widget配置的AppIntent,让用户选择具体的TaskItem:
import AppIntents struct SelectTaskIntent: AppIntent, WidgetConfigurationIntent { static var title: LocalizedStringResource = "选择任务" static var description: IntentDescription = .init(stringLiteral: "选择要在Widget中展示的任务") // 配置选择参数,关联TaskItem实体 @Parameter(title: "任务") var task: TaskItem }
4. Widget实现
使用IntentConfiguration关联上述Intent,实现可配置的Widget:
import WidgetKit import SwiftUI struct TaskWidget: Widget { let kind: String = "TaskWidget" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: SelectTaskIntent.self, provider: Provider()) { entry in TaskWidgetEntryView(entry: entry) } .configurationDisplayName("任务Widget") .description("展示你选择的任务详情") .supportedFamilies([.systemSmall, .systemMedium]) } } // Widget数据提供者 struct Provider: IntentTimelineProvider { func placeholder(in context: Context) -> TaskEntry { TaskEntry(date: Date(), task: TaskItem(title: "示例任务", content: "这是一个示例任务内容")) } func getSnapshot(for configuration: SelectTaskIntent, in context: Context, completion: @escaping (TaskEntry) -> ()) { let entry = TaskEntry(date: Date(), task: configuration.task) completion(entry) } func getTimeline(for configuration: SelectTaskIntent, in context: Context, completion: @escaping (Timeline<TaskEntry>) -> ()) { let entries = [TaskEntry(date: Date(), task: configuration.task)] let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } // Widget条目模型 struct TaskEntry: TimelineEntry { let date: Date let task: TaskItem } // Widget界面 struct TaskWidgetEntryView: View { var entry: Provider.Entry var body: some View { VStack(alignment: .leading, spacing: 8) { Text(entry.task.title) .font(.headline) .strikethrough(entry.task.isCompleted) Text(entry.task.content) .font(.caption) .foregroundColor(.secondary) } .padding() } }
原示例代码(中文翻译版)
struct 选择咖啡Intent: AppIntent, WidgetConfigurationIntent { static var 标题: LocalizedStringResource = "选择咖啡" static var 描述: IntentDescription = .init(stringLiteral: "选择一款咖啡") @Parameter(title: "咖啡") var 任务: 咖啡 } struct 咖啡: Equatable, Hashable, AppEntity { static var 类型展示标识: TypeDisplayRepresentation = "Widget选择咖啡列表" typealias 默认查询类型 = 咖啡查询 static var 默认查询: 咖啡查询 = 咖啡查询() static var 类型显示名称: LocalizedStringResource = LocalizedStringResource("咖啡", defaultValue: "咖啡") var 展示信息: DisplayRepresentation { DisplayRepresentation(title: .init(stringLiteral: 名称)) } let id: UUID let 名称: String let 图片链接: URL } struct 咖啡查询: EntityStringQuery { typealias 实体 = 咖啡 func 匹配实体(匹配字符串 string: String) async throws -> [咖啡] { return [] } func 根据ID获取实体(标识符 identifiers: [UUID]) async throws -> [咖啡] { return [ ] } }
内容的提问来源于stack exchange,提问作者Ammar Ahmad
相关产品推荐
相关产品推荐

