Как обновить представление SwiftUI, если основное хранилище данных было обновлено с помощью фонового расширения?
У меня есть приложение SwiftUI с целью расширения намерений. У меня также есть основное хранилище данных, расположенное в общей группе приложений.
Расширение намерений может успешно записывать данные в магазин, используя newBackgroundContext()
в приложении "Ярлыки". Мое приложение SwiftUI может считывать данные из группы приложений вviewContext
но только когда я принудительно закрываю приложение и снова открываю его.
Я думаю, мне нужен способ сообщить моему приложению, что оно было обновлено в другом контексте, но я не уверен, как это сделать?
Я пробовал установить context.automaticallyMergesChangesFromParent = true
в делегате приложения и добавив context.refreshAllObjects()
к onAppear
в представлении SwiftUI, но, похоже, это не имеет никакого значения.
mergeChanges(fromContextDidSave:) кажется многообещающим, но я не уверен, как получить уведомление.
Я новичок в Core Data и очень запутался, поэтому любые указатели в правильном направлении были бы замечательными, спасибо!
Вот очищенная версия моего текущего кода:
Делегат приложения
lazy var persistentContainer: NSCustomPersistentContainer = {
let container = NSCustomPersistentContainer(name: "exampleContainerName")
container.loadPersistentStores { description, error in
if let error = error {
print("error loading persistent core data store")
}
}
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
print("error saving core data context")
}
}
}
class NSCustomPersistentContainer: NSPersistentContainer {
override open class func defaultDirectoryURL() -> URL {
var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "exampleAppGroupName")
storeURL = storeURL?.appendingPathComponent("exampleContainerName")
return storeURL!
}
}
Делегат сцены
guard let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext else {
fatalError("Unable to read managed object context.")
}
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ExampleView.environment(\.managedObjectContext, context))
self.window = window
window.makeKeyAndVisible()
}
Swift UI View
import SwiftUI
import CoreData
struct ExampleView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(
entity: ExampleEntityName.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \ExampleEntityName.date, ascending: false),
]
) var items: FetchedResults<ExampleEntityName>
var body: some View {
VStack {
List(items, id: \.self) { item in
Text("\(item.name)")
}
}
}
}
Расширение намерения
class NSCustomPersistentContainer: NSPersistentContainer {
override open class func defaultDirectoryURL() -> URL {
var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "exampleContainerName")
storeURL = storeURL?.appendingPathComponent("exampleAppGroupName")
return storeURL!
}
}
let persistentContainer: NSCustomPersistentContainer = {
let container = NSCustomPersistentContainer(name: "exampleContainerName")
container.loadPersistentStores { description, error in
if let error = error {
print("error loading persistent core data store: \(error)")
}
}
return container
}()
let context = persistentContainer.newBackgroundContext()
let entity = NSEntityDescription.entity(forEntityName: "ExampleEntityName", in: context)
let newEntry = NSManagedObject(entity: entity!, insertInto: context)
newEntry.setValue(Date(), forKey: "date")
newEntry.setValue("Hello World", forKey: "name")
do {
try context.save()
} catch {
print("Failed saving")
}
2 ответа
Не знаю, нашли ли вы ответ, но у меня была аналогичная проблема с использованием UserDefaults и расширения намерения в общей группе приложений. В конечном итоге у меня сработало добавление вызова для загрузки элементов в методе ниже в SceneDelegate:
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
SiriResponseManager.shared.grabUD()
}
В моем примере я использую синглтон, содержащий метод загрузки элементов из UserDefaults. Этот одноэлементный класс является ObservableObject. В моем первом загружаемом представлении (ContentView в моем случае) я назначаю переменную ObservedObject своему классу синглтона и получаю переменные @State в моем синглтоне, чтобы заполнить строку в моем представлении.
Надеюсь, это поможет.
class Data: ObservableObject {
@Published var dataToUpdate: Int
}
struct UpdateView : View {
@EnvironmentObject data: Data
...body ... {
// use data.dataToUpdate here
}
}
Просто определите данные, как в этом примере. Затем заполняйте dataToUpdate всякий раз, когда ваша база данных изменяется... конечно, это просто упрощенный пример только с Int Value...