SwiftUI CloudKit 保持活动状态时不刷新视图
SwiftUI CloudKit not refreshing view when remaining active
我正在使用 SwiftUI 开发 macOS 和 iOS 应用程序。两者都使用 CoreData 和 iCloudKit 在两个平台之间同步数据。它确实与同一个 iCloud 容器一起工作得很好。
我遇到一个问题,就是停留在应用程序中时没有触发iCloud后台更新。如果我在两个系统上都进行了更改,则会推送更改,但在其他设备上不可见。
我需要重新加载应用程序,关闭应用程序并再次打开,或者在我的 Mac 应用程序中失去焦点然后返回。那我的List
就要刷新了。我不确定为什么它不起作用,同时留在应用程序中而不会失去焦点。
我在 Whosebug 中阅读了几个线程,但它们对我不起作用。这是我在 iOS
中的简单视图
struct ContentView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@State private var refreshing = false
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
@FetchRequest(entity: Person.entity(), sortDescriptors: []) var persons : FetchedResults<Person>
var body: some View {
NavigationView
{
List()
{
ForEach(self.persons, id:\.self) { person in
Text(person.firstName + (self.refreshing ? "" : ""))
// here is the listener for published context event
.onReceive(self.didSave) { _ in
self.refreshing.toggle()
}
}
}
.navigationBarTitle(Text("Person"))
}
}
}
在此示例中,我已经在使用变通方法, 在另一个问题中进行了描述。但是,这对我也不起作用。列表未刷新。
在日志中我可以看到它没有对 iCloud 执行 ping 操作以进行刷新。只有当我重新打开应用程序时。为什么后台模式不起作用?我已正确激活所有内容并设置我的 AppDelegate。
lazy var persistentContainer: NSPersistentCloudKitContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
container.persistentStoreDescriptions.forEach { storeDesc in
storeDesc.shouldMigrateStoreAutomatically = true
storeDesc.shouldInferMappingModelAutomatically = true
}
//let container = NSPersistentCloudKitContainer(name: "NAME")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
UIApplication.shared.registerForRemoteNotifications()
return container
}()
编辑:
我的 iOS 应用程序在重新打开时只会继续从 iCloud 获取记录。看这个动图:
所以除了我的评论和更多信息之外,我怀疑你没有正确设置你的项目。
在 Signings and Capabilities 下,您的项目应该与此类似...
如前所述,我怀疑您的 ContentView 视图中的很多代码都是不必要的。尝试删除通知并简化您的视图代码,例如...
struct ContentView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(entity: Person.entity(),
sortDescriptors: []
) var persons : FetchedResults<Person>
var body: some View {
NavigationView
{
List()
{
ForEach(self.persons) { person in
Text(person.firstName)
}
}
.navigationBarTitle(Text("Person"))
}
}
}
如果您的项目设置正确,CloudKit 应该会处理必要的通知,并且 @FetchRequest
属性 包装器将更新您的数据集。
另外,因为每个 Core Data 实体默认是 Identifiable
,所以没有必要在你的 ForEach
语句中引用 id:\.self
,所以不用...
ForEach(self.persons, id:\.self) { person in
你应该可以使用...
ForEach(self.persons) { person in
如评论中所述,您在 var persistentContainer
中包含了不必要的代码。它应该像这样工作...
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "NAME")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
return container
}()
对于最近看到这个的任何人来说,经过大量搜索后对我来说修复只是简单地在 Persistence.swift 的 init(inMemory) 方法中添加 container.viewContext.automaticallyMergesChangesFromParent = true
(Apple 的所有股票用于 SwiftUI在 Xcode 12.5.1 中。一旦我添加了它并重建了 2 个模拟器,一切都同步了 w/in 5-15 秒。
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "StoreName")
container.viewContext.automaticallyMergesChangesFromParent = true
我正在使用 SwiftUI 开发 macOS 和 iOS 应用程序。两者都使用 CoreData 和 iCloudKit 在两个平台之间同步数据。它确实与同一个 iCloud 容器一起工作得很好。
我遇到一个问题,就是停留在应用程序中时没有触发iCloud后台更新。如果我在两个系统上都进行了更改,则会推送更改,但在其他设备上不可见。
我需要重新加载应用程序,关闭应用程序并再次打开,或者在我的 Mac 应用程序中失去焦点然后返回。那我的List
就要刷新了。我不确定为什么它不起作用,同时留在应用程序中而不会失去焦点。
我在 Whosebug 中阅读了几个线程,但它们对我不起作用。这是我在 iOS
中的简单视图struct ContentView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@State private var refreshing = false
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
@FetchRequest(entity: Person.entity(), sortDescriptors: []) var persons : FetchedResults<Person>
var body: some View {
NavigationView
{
List()
{
ForEach(self.persons, id:\.self) { person in
Text(person.firstName + (self.refreshing ? "" : ""))
// here is the listener for published context event
.onReceive(self.didSave) { _ in
self.refreshing.toggle()
}
}
}
.navigationBarTitle(Text("Person"))
}
}
}
在此示例中,我已经在使用变通方法,
在日志中我可以看到它没有对 iCloud 执行 ping 操作以进行刷新。只有当我重新打开应用程序时。为什么后台模式不起作用?我已正确激活所有内容并设置我的 AppDelegate。
lazy var persistentContainer: NSPersistentCloudKitContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
container.persistentStoreDescriptions.forEach { storeDesc in
storeDesc.shouldMigrateStoreAutomatically = true
storeDesc.shouldInferMappingModelAutomatically = true
}
//let container = NSPersistentCloudKitContainer(name: "NAME")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
UIApplication.shared.registerForRemoteNotifications()
return container
}()
编辑:
我的 iOS 应用程序在重新打开时只会继续从 iCloud 获取记录。看这个动图:
所以除了我的评论和更多信息之外,我怀疑你没有正确设置你的项目。
在 Signings and Capabilities 下,您的项目应该与此类似...
如前所述,我怀疑您的 ContentView 视图中的很多代码都是不必要的。尝试删除通知并简化您的视图代码,例如...
struct ContentView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(entity: Person.entity(),
sortDescriptors: []
) var persons : FetchedResults<Person>
var body: some View {
NavigationView
{
List()
{
ForEach(self.persons) { person in
Text(person.firstName)
}
}
.navigationBarTitle(Text("Person"))
}
}
}
如果您的项目设置正确,CloudKit 应该会处理必要的通知,并且 @FetchRequest
属性 包装器将更新您的数据集。
另外,因为每个 Core Data 实体默认是 Identifiable
,所以没有必要在你的 ForEach
语句中引用 id:\.self
,所以不用...
ForEach(self.persons, id:\.self) { person in
你应该可以使用...
ForEach(self.persons) { person in
如评论中所述,您在 var persistentContainer
中包含了不必要的代码。它应该像这样工作...
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "NAME")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
return container
}()
对于最近看到这个的任何人来说,经过大量搜索后对我来说修复只是简单地在 Persistence.swift 的 init(inMemory) 方法中添加 container.viewContext.automaticallyMergesChangesFromParent = true
(Apple 的所有股票用于 SwiftUI在 Xcode 12.5.1 中。一旦我添加了它并重建了 2 个模拟器,一切都同步了 w/in 5-15 秒。
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "StoreName")
container.viewContext.automaticallyMergesChangesFromParent = true