如何在 IntentConfiguration Widget view iOS 14 中显示与 CoreData 中同一行相关的数据?
How to display data related to the same row in CoreData at IntentConfiguration Widget view iOS 14?
我目前正在使用 SwiftUI
开发应用程序并尝试让 widget ios 14
用户可以使用 IntentConfiguration
选择数据来检查其详细信息
我想在这个应用程序中做的大纲如下:
- 用户在添加视图中添加了一些数据(id:
UUID
,任务:String
,状态:String
,还有一些...)到
CoreData
在主机应用中
- 用户选择了用户想要在编辑小部件中查看其详细信息的数据
- 用户可以在小部件视图中查看简要详细信息
- 如果用户点击小部件视图,可以在宿主应用程序的详细视图中查看详细数据
在我的代码中,我几乎可以实现上面解释的所有功能。
但我不知道如何在编辑小部件中显示任务数据...
到目前为止,我将CoreData
中的UUID数据指定为配置中的参数。因为 WidgetEntryView
需要 UUID
(或某些唯一值)来过滤哪些 row
请求 CoreData
并使 URL
为 DeepLink
进行详细查看在主机应用程序中。
因此小部件中的列表视图显示 UUID 数据,如下所示。
但我想在该列表视图中显示任务数据而不是 UUID,同时将 UUID 数据也提供给 Widget 视图。
如果我将 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, date: Date(), timerEntity: timerEntity!)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, 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
:
现在您的 Item
具有 identifier
和 displayString
属性,可以映射到模型的 UUID
和 task
属性。
然后,在 IntentHandler
而不是 INObjectCollection<NSString>?
中,您需要在完成中提供 INObjectCollection<Item>?
。
假设您已经从核心数据中获取了 results
,您只需要将它们映射到 Item
对象:
let results = try moc.fetch(request)
let items = results.map {
Item(identifier: [=10=].id.uuidString, display: [=10=].task)
}
completion(items, nil)
这样你可以使用 display
属性 向用户显示 可读的 信息,但也有 identifier
属性 稍后可用于检索核心数据模型。
我目前正在使用 SwiftUI
开发应用程序并尝试让 widget ios 14
用户可以使用 IntentConfiguration
我想在这个应用程序中做的大纲如下:
- 用户在添加视图中添加了一些数据(id:
UUID
,任务:String
,状态:String
,还有一些...)到CoreData
在主机应用中 - 用户选择了用户想要在编辑小部件中查看其详细信息的数据
- 用户可以在小部件视图中查看简要详细信息
- 如果用户点击小部件视图,可以在宿主应用程序的详细视图中查看详细数据
在我的代码中,我几乎可以实现上面解释的所有功能。
但我不知道如何在编辑小部件中显示任务数据...
到目前为止,我将CoreData
中的UUID数据指定为配置中的参数。因为 WidgetEntryView
需要 UUID
(或某些唯一值)来过滤哪些 row
请求 CoreData
并使 URL
为 DeepLink
进行详细查看在主机应用程序中。
因此小部件中的列表视图显示 UUID 数据,如下所示。
但我想在该列表视图中显示任务数据而不是 UUID,同时将 UUID 数据也提供给 Widget 视图。
如果我将 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, date: Date(), timerEntity: timerEntity!)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, 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
:
现在您的 Item
具有 identifier
和 displayString
属性,可以映射到模型的 UUID
和 task
属性。
然后,在 IntentHandler
而不是 INObjectCollection<NSString>?
中,您需要在完成中提供 INObjectCollection<Item>?
。
假设您已经从核心数据中获取了 results
,您只需要将它们映射到 Item
对象:
let results = try moc.fetch(request)
let items = results.map {
Item(identifier: [=10=].id.uuidString, display: [=10=].task)
}
completion(items, nil)
这样你可以使用 display
属性 向用户显示 可读的 信息,但也有 identifier
属性 稍后可用于检索核心数据模型。