SwiftUI:删除所有核心数据实体条目后,列表不会自动更新

Kuh*_*ann 13 core-data swift swiftui

我知道 SwiftUI 使用状态驱动渲染。所以我假设,当我删除核心数据实体条目时,我的包含核心数据元素的列表会立即刷新。我使用此代码,它成功地清理了我的实体:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

为了让我的视图中的列表视觉上为空,我必须在之后离开视图(例如使用“self.presentationMode.wrappedValue.dismiss()”)并再次打开它。好像这些值仍然存储在内存中的某个地方。这当然不是用户友好的,我确信我只是监督了一些可以立即刷新列表的内容。也许有人可以帮忙。

Asp*_*eri 16

原因是execute(如下详述-注意第一句话)不会影响托管对象上下文,因此所有获取的对象都保留在上下文中,而 UI 代表上下文真正呈现的内容。

因此,一般而言,在此批量操作之后,您需要通知该代码(此处未提供)强制同步并重新获取所有内容。

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
Run Code Online (Sandbox Code Playgroud)

例如它可能是以下方法(草率)

// 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
Run Code Online (Sandbox Code Playgroud)

  • 这不是最好的解决方案,因为 .id 将重新生成整个列表,而不仅仅是更新更改 (2认同)

Tob*_*ias 13

无需强制刷新,这是 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])
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是有关如何调用它的代码的更新:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

在此链接下还有更多信息:Core Data NSBatchDeleteRequest 似乎将对象保留在上下文中