@FetchRequest 上有很多问题,其中的答案提供了强制 SwiftUI 重绘 View 的变通方法。我找不到任何解决这个问题的方法。
我的应用程序使用共享扩展将新项目插入我的 CoreData
模型(sqlite)。在主应用程序和共享扩展目标之间共享一个框架。 SwiftUI 主 ContentView
在我的应用程序中使用 @FetchRequest
属性来监视数据库的内容并更新 View :
struct ContentView: View {
@FetchRequest(fetchRequest: MyObj.allFetchRequest()) var allObjs: FetchedResults<MyObj>
...
}
我的 xcdatamodel 扩展为:
public static func allFetchRequest() -> NSFetchRequest<MyObj> {
let request: NSFetchRequest<MyObj> = MyObj.fetchRequest()
// update request to sort by name
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
return request
}
共享扩展使用以下命令添加新项目:
let newObj = MyObj(context: context)
newObj.name = "new name"
try! context.save()
使用共享扩展添加新项目后,当我通过在设备上切换应用程序返回我的应用程序时,
SceneDelegate
检测到 sceneDidBecomeActive
.在这个函数中,我可以手动运行一个 fetch 请求并查看新项目是否已添加。我什至可以使用其他 SO 问题的答案来欺骗 UI 在 ContentView 中重建(通过在收到通知时切换构建器中使用的 Bool),但无论哪种情况,当 UI 重建时,allObjs
不包含来自数据库的更新结果。在我的
sceneDidBecomeActive
我已经尝试了多种代码组合,以尝试以 @FetchRequest
的方式使用数据库的最新内容更新上下文。会看到的。数据在那里,我的托管上下文可以看到这个新数据,因为手动获取请求返回新项目,但是 @FetchRequest
总是过时阻止 UI 重建。在这两个位置,我都打印了上下文,并且确信它是相同的上下文。我试过了:context.reset()
context.refreshAllObjects()
context.save()
我不知道为什么
@FetchRequest
ContentView
时不更新其值重绘。如何强制
@FetchRequest
在我的共享扩展将新对象添加到数据库后更新其对象并导致 SwiftUI 重建?(Xcode 11.4.1,Swift 5,目标:iOS 13.1)
编辑:
在我的
ContentView
,我打印 allObjs
变量、它们的状态、上下文的内存地址等。我将其与从上下文中获取的新值进行比较,现有对象的所有值都是相同的,除了新的获取显示新添加的对象,而 allObjs
不包含新对象。很明显 @FetchRequest
对象未更新。编辑 2:
我尝试创建自定义
ObservableObject
带有 @Published
的类属性和显式 update()
方法,然后将其用作 @ObservedObject
在 ContentView
.这行得通。class MyObjList: ObservableObject {
static let shared = MyObjList()
@Published var allObjs: [MyObj]!
init() {
update()
}
func update() {
allObjs = try! DataStore.persistentContainer.viewContext.fetch(MyObj.allFetchRequest())
}
}
然后在
ContentView
:@EnvironmentObject var allObjs: MyObjList
这有助于确认数据存在,它只是没有被
@FetchRequest
正确更新.这是一个功能性的解决方法。
最佳答案
我使用持久历史跟踪和远程更改通知解决了类似的问题。这些使 Core Data 记录 SQLite 存储发生的每一个更改,并在这些更改之一远程发生时(例如,从扩展程序或 iCloud)向您发送通知。
默认情况下不启用这些功能。您必须在加载持久存储之前配置它们:
persistentStoreDescriptions.forEach {
$0.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
$0.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
}
然后订阅远程更改通知:NotificationCenter.default.addObserver(
/* The persistent container/view model/MyObjList/etc that acts as observer */,
selector: #selector(coordinatorReceivedRemoteChanges(_:)),
name: .NSPersistentStoreRemoteChange,
object: persistentStoreCoordinator // don't forget to pass the PSC as the object!
)
现在我们会在发生远程更改时收到通知,但我们需要在整个应用程序中与托管对象上下文共享这些更改。这对于每个应用程序来说看起来都不同:有时您的 MOC 根本不需要知道这些变化,有时他们只需要了解其中的一些,有时是全部。苹果最近published a guide深入探讨了如何确保只处理相关的更改。在这种情况下,您可能会这样做:
@objc
func coordinatorReceivedRemoteChanges(_ notification: Notification) {
let currentToken = notification.userInfo?[NSPersistentHistoryTokenKey] as? NSPersistentHistoryToken
let existingToken = ... // retrieve the previously processed history token so we don't repeat work; you might have saved it to disk using NSKeyedArchiver
// Fetch the history
let historyRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: existingToken)
let result = try! context.execute(historyRequest) as! NSPersistentHistoryResult
let transactions = result.result as! [NSPersistentHistoryTransaction]
// Filter out the non-relevant changes
for transaction in transaction {
// Maybe we only care about changes to MyObjs
guard transaction.entityDescription.name == MyObj.entity().name else { continue }
// Maybe we only care about specific kinds of changes
guard let changes = transaction.changes, changes.contains(where: { ... }) else { continue }
// Merge transaction into the view context
viewContext.mergeChanges(fromContextDidSave: transaction.objectIDNotification())
}
// Finally, save `currentToken` so it can be used next time and optionally delete old history.
}
任意 @FetchRequest
使用 viewContext
现在将接收所有合并的更改并相应地刷新其数据。
关于ios - 当更改源为共享扩展时,@FetchRequest 结果未在 SwiftUI 中更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61679939/