SwiftUI: список не обновляется автоматически после удаления всех записей основного объекта данных - PullRequest
4 голосов
/ 14 февраля 2020

Я знаю, что SwiftUI использует управляемый состоянием рендеринг. Поэтому при удалении записей Core Data Entity я предполагал, что мой список с элементами Core Data обновляется немедленно. Я использую этот код, который успешно очищает мою сущность:

func deleteAll()
{
    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = ToDoItem.fetchRequest()
    let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

    let persistentContainer = (UIApplication.shared.delegate as! AppDelegate).persistentContainer

    do {
        try persistentContainer.viewContext.execute(deleteRequest)
    } catch let error as NSError {
        print(error)
    }
}

Чтобы получить список в моем представлении визуально пустым, я должен впоследствии оставить представление (например, с помощью "self.presentationMode.wrappedValue.dismiss ( ) ") и откройте его снова. Как будто значения все еще хранятся где-то в памяти или что-то. Это, конечно, не удобно для пользователя, и я уверен, что просто наблюдаю за тем, что немедленно обновляет Список. Может быть, кто-то может помочь.

Ответы [ 2 ]

3 голосов
/ 17 февраля 2020

Нет необходимости форсировать refre sh, это IMO не чистое решение.

Как вы правильно упомянули в своем вопросе, в памяти все еще есть элементы. Решение состоит в том, чтобы обновить объекты в памяти после выполнения с помощью mergeChanges.

В этом сообщении в блоге подробно объясняется решение в разделе «Обновление объектов в памяти».

Там автор предоставляет расширение NSBatchDeleteRequest следующим образом

extension NSManagedObjectContext {

/// Executes the given `NSBatchDeleteRequest` and directly merges the changes to bring the given managed object context up to date.
///
/// - Parameter batchDeleteRequest: The `NSBatchDeleteRequest` to execute.
/// - Throws: An error if anything went wrong executing the batch deletion.
public func executeAndMergeChanges(using batchDeleteRequest: NSBatchDeleteRequest) throws {
    batchDeleteRequest.resultType = .resultTypeObjectIDs
    let result = try execute(batchDeleteRequest) as? NSBatchDeleteResult
    let changes: [AnyHashable: Any] = [NSDeletedObjectsKey: result?.result as? [NSManagedObjectID] ?? []]
    NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
}

}

Вот обновление вашего кода о том, как его вызвать:

func deleteAll() {
    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = ToDoItem.fetchRequest()
    let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

    let persistentContainer = (UIApplication.shared.delegate as! AppDelegate).persistentContainer

    do {
        try persistentContainer.viewContext.executeAndMergeChanges(deleteRequest)
    } catch let error as NSError {
        print(error)
    }
}

Здесь также есть дополнительная информация по этой ссылке: Базовые данные NSBatchDeleteRequest оставляет объекты в контексте .

3 голосов
/ 14 февраля 2020

Причина в том, что execute (как подробно описано ниже - обратите внимание на первое предложение) не влияет на контекст управляемых объектов, поэтому все извлеченные объекты остаются в контексте, а пользовательский интерфейс представляет то, что действительно представлено контекстом.

Таким образом, в общем, после этой массовой операции вам нужно сообщить обратно к этому коду (не предоставленному здесь) принудительную синхронизацию c и перезапустить все.

Объявление интерфейса API

// Method to pass a request to the store without affecting the contents of the managed object context.
// Will return an NSPersistentStoreResult which may contain additional information about the result of the action
// (ie a batch update result may contain the object IDs of the objects that were modified during the update).
// A request may succeed in some stores and fail in others. In this case, the error will contain information
// about each individual store failure.
// Will always reject NSSaveChangesRequests.
@available(iOS 8.0, *)
open func execute(_ request: NSPersistentStoreRequest) throws -> NSPersistentStoreResult

Например, это может быть следующий подход (неуклюжий)

// somewhere in View declaration
@State private var refreshingID = UUID()

...
// somewhere in presenting fetch results
ForEach(fetchedResults) { item in
    ...
}.id(refreshingID) // < unique id of fetched results

...

// somewhere in bulk delete 
try context.save() // < better to save everything pending
try context.execute(deleteRequest)
context.reset() // < reset context
self.refreshingID = UUID() // < force refresh
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...