保持活动状态时,SwiftUI CloudKit不会刷新视图

时间:2020-09-06 12:20:02

标签: ios swift core-data swiftui icloud

我正在使用SwiftUI开发macOS和iOS应用。两者都使用CoreData和iCloudKit在两个平台之间同步数据。在同一个iCloud容器上确实可以很好地工作。

我遇到一个问题,即留在应用程序中时不会触发iCloud后台更新。如果我在两个系统上都进行了更改,则更改将被推送,但是在其他设备上不可见。

我需要重新加载应用程序,关闭该应用程序,然后再次打开,或者失去对Mac应用程序的关注,然后重新使用它。然后我的List将被刷新。我不确定为什么它在保持在应用程序内部而不会失去焦点的同时不起作用。

我在Stackoverflow中阅读了多个线程,但是它们对我不起作用。这是我在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"))
        }
    }
}

在此示例中,我已经在使用一种解决方法,在另一个问题中描述了Asperi。但是,这对我也不起作用。该列表未刷新。

在日志中,我可以看到它没有对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提取记录。看到这个gif:

1 个答案:

答案 0 :(得分:2)

因此,除了我的评论且没有更多信息,我怀疑您没有正确设置项目。

在“签名和功能”下,您的项目应与此类似...

Project signing 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,因此无需在您的id:\.self语句中引用ForEach,所以代替...

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
}()