Как обновить представление 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...

Другие вопросы по тегам