В настоящее время я разрабатываю приложение с использованием SwiftUI
и пытаюсь заставить widget ios 14
пользователя выбирать данные для проверки их деталей с помощью IntentConfiguration
Схема, которую я хочу сделать в этом приложении, выглядит следующим образом:
- Пользователь добавляет некоторые данные в добавляющем представлении (идентификатор:
UUID
, задача:String
, статус:String
и некоторые ...) вCoreData
в хост-приложении - Пользователь выбирает данные, которые он хочет проверить, в виджете редактирования.
- Пользователь может просмотреть краткую информацию в представлении виджета.
- Если пользователь коснется представления виджета, он сможет проверить подробные данные в подробном представлении в главном приложении.
В своих кодах я мог реализовать почти все функции, которые я объяснил выше.
Но я не знаю, как мне отобразить данные задачи в виджете редактирования ...
Пока что я указываю данные UUID в CoreData
как параметр в конфигурации. Потому что 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