You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 19:31:07