问题描述
我目前正在使用 SwiftUI
开发应用程序,并试图让 widget ios 14
用户可以选择数据以使用 IntentConfiguration
- 用户在添加视图(id:
UUID
、任务:String
、状态:String
和一些...)中添加了一些数据到CoreData
在宿主应用程序中 - 用户在编辑小部件中选择要查看其详细信息的数据
- 用户可以在小部件视图中查看简要详细信息
- 如果用户点击小部件视图,可以在宿主应用程序的详细视图中查看详细数据
但我不知道如何在编辑小部件中显示任务数据...
到目前为止,我将 CoreData
中的 UUID 数据指定为配置中的参数。因为 WidgetEntryView
需要 UUID
(或一些唯一值)来过滤哪些 row
请求到 CoreData
并为 URL
制作 DeepLink
以进行详细视图在宿主应用中。
因此小部件中的列表视图显示 UUID 数据,如下所示。
但我想在该列表视图中显示任务数据而不是 UUID,同时将 UUID 数据提供给小部件视图。
如果我将 CoreData
中的任务数据指定为配置中的参数,小部件将任务显示为列表。但在这种情况下,过滤到 Coredata
(nspredicate(format: "id == %@"
) 和 widgetURL()
的请求数据不起作用...
我该如何实施?
代码如下:
TimerIntentWidget.swift
import Foundation
import WidgetKit
import SwiftUI
import CoreData
struct Provider: IntentTimelineProvider {
typealias Intent = ConfigurationIntent
var moc = PersistenceController.shared.managedobjectContext
init(context : NSManagedobjectContext) {
self.moc = context
}
func placeholder(in context: Context) -> SimpleEntry {
var timerEntity:TimerEntity?
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
return SimpleEntry(configuration: ConfigurationIntent(),date: Date(),timerEntity: timerEntity!)
}
func getSnapshot(for configuration: ConfigurationIntent,in context: Context,completion: @escaping (SimpleEntry) -> ()) {
var timerEntity:TimerEntity?
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
let entry = SimpleEntry(configuration: configuration,timerEntity: timerEntity!)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent,completion: @escaping (Timeline<Entry>) -> ()) {
var timerEntity:TimerEntity?
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
request.predicate = nspredicate(format: "id == %@",UUID(uuidString: configuration.UUID!)! as CVararg)
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
var entries: [SimpleEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour,value: hourOffset,to: currentDate)!
let entry = SimpleEntry(configuration: configuration,date: entryDate,timerEntity: timerEntity!)
entries.append(entry)
}
let timeline = Timeline(entries: entries,policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let configuration: ConfigurationIntent
let date: Date
let timerEntity:TimerEntity?
}
struct TimerIntentWidgetEntryView : View{
var entry: Provider.Entry
var body: some View {
vstack{
Text(entry.timerEntity!.id!.uuidString)
Divider()
Text(entry.timerEntity!.task!)
Divider()
Text(entry.timerEntity!.status!)
Divider()
Text(entry.date,style: .time)
}
.widgetURL(makeURLScheme(id: entry.timerEntity!.id!))
}
}
@main
struct TimerIntentWidget: Widget {
let kind: String = "TimerIntentWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,intent: ConfigurationIntent.self,provider: Provider(context: PersistenceController.shared.managedobjectContext)) { entry in
TimerIntentWidgetEntryView(entry: entry)
.environment(\.managedobjectContext,PersistenceController.shared.managedobjectContext)
}
.configurationdisplayName("My Widget")
.description("This is an example widget.")
}
}
func makeURLScheme(id: UUID) -> URL? {
guard let url = URL(string: "timerlist://detail") else {
return nil
}
var urlComponents = URLComponents(url: url,resolvingAgainstBaseURL: true)
urlComponents?.queryItems = [URLQueryItem(name: "id",value: id.uuidString)]
return urlComponents?.url
}
IntentHandler.swift
import WidgetKit
import SwiftUI
import CoreData
import Intents
class IntentHandler: INExtension,ConfigurationIntentHandling {
var moc = PersistenceController.shared.managedobjectContext
func provideUUIDOptionsCollection(for intent: ConfigurationIntent,with completion: @escaping (INObjectCollection<Nsstring>?,Error?) -> Void) {
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
var nameIdentifiers:[Nsstring] = []
do{
let results = try moc.fetch(request)
for result in results{
nameIdentifiers.append(Nsstring(string: result.id?.uuidString ?? ""))
}
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
let allNameIdentifiers = INObjectCollection(items: nameIdentifiers)
completion(allNameIdentifiers,nil)
}
override func handler(for intent: INIntent) -> Any {
return self
}
}
TimerIntentWidget.intentdeFinition
Persistence.swift(宿主应用程序)
import CoreData
class PersistenceController {
static let shared = PersistenceController()
private init() {}
private let persistentContainer: NSPersistentContainer = {
let storeURL = FileManager.appGroupContainerURL.appendingPathComponent("TimerEntity")
let container = NSPersistentContainer(name: "ListTimer")
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
container.loadPersistentStores(completionHandler: { storeDescription,error in
if let error = error as NSError? {
print(error.localizedDescription)
}
})
return container
}()
}
extension PersistenceController {
var managedobjectContext: NSManagedobjectContext {
persistentContainer.viewContext
}
}
extension PersistenceController {
var workingContext: NSManagedobjectContext {
let context = NSManagedobjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = managedobjectContext
return context
}
}
import Foundation
extension FileManager {
static let appGroupContainerURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.com.sample.ListTimer")!
}
Xcode:版本 12.0.1
iOS:14.0
生命周期:SwiftUI 应用
解决方法
您可以为配置参数创建自定义类型。目前您使用的是 String
,它限制您只能使用一个值。
而是创建一个自定义类型,我们称之为 Item
:
现在您拥有 identifier
的 displayString
和 Item
属性,它们可以映射到模型的 UUID
和 task
属性。>
然后,在 IntentHandler
而不是 INObjectCollection<NSString>?
中,您需要在补全中提供 INObjectCollection<Item>?
。
假设您已经从 Core Data 中提取了 results
,您只需要将它们映射到 Item
对象:
let results = try moc.fetch(request)
let items = results.map {
Item(identifier: $0.id.uuidString,display: $0.task)
}
completion(items,nil)
通过这种方式,您可以使用 display
属性向用户显示可读信息,但也可以使用 identifier
属性稍后用于检索核心数据模型.