【问题标题】:SwiftUI CloudKit not refreshing view when remaining activeSwiftUI CloudKit 在保持活动状态时不刷新视图
【发布时间】:2023-03-09 05:28:01
【问题描述】:

我正在使用 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 在另一个问题中进行了描述。但是,这对我也不起作用。列表未刷新。

在日志中,我可以看到它没有 ping iCloud 进行刷新。只有当我重新打开应用程序时。为什么后台模式不起作用?我已正确激活所有内容并设置了我的 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:

【问题讨论】:

  • 一些副业 cmets...当您在问题中包含样板代码时,无需包含 Apple 包含的警告...还有 .shouldMigrateStoreAutomatically.shouldInferMappingModelAutomatically 都默认为 @987654328 @ 所以这些电话是必要的......最后,您无需致电UIApplication.shared.registerForRemoteNotifications(),因为您应该在“后台模式”下的“签名和功能”选项卡中检查这一点。
  • 核心数据实体符合 ObservableObject 并且默认发布,所以通知对我来说似乎完全没有必要。也许我误解了你的需要?

标签: ios swift core-data swiftui icloud


【解决方案1】:

所以除了我的 cmets 并且没有更多信息,我怀疑你没有正确设置你的项目。

在 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

如 cmets 中所述,您在 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
}()

【讨论】:

  • 感谢您非常详细的回答。我已经像你说的那样编辑了所有内容。你是对的,我像你说的那样检查了容器的默认设置。但是,它仍然无法正常工作。我总是必须重新启动应用程序,然后它会获取更新。 macOS 或 iOS 上没有自动获取。我已经配置了远程通知并按照您所说的设置了所有内容。您能分享您的权利文件吗?
  • 权利文件不会告诉你太多,它们只是保存iCloud Container IdentifiersiCloud Services 的值。我目前正在开发一个完全用 SwiftUI 编写的通用项目,该项目针对 iOS 和 macOS,使用 Core Data 和 CloudKit。我正在使用NSPersistentCloudKitContainer - 这非常适合在我的 Mac、iPad Pro 和模拟器之间同步数据。所以我谦虚地建议你没有正确设置你的项目。涉及的因素太多,没有更多的信息,我很难提供建议。
  • 我可以推荐两个相当不错的教程,它们应该可以帮助您检查您的项目设置...按照第一个教程 alfianlosari.com/posts/… 为 iOS 准备应用程序,然后按照第二个教程添加 macOS 目标alfianlosari.com/posts/…。第二个教程重点介绍建立正常运行的NSPersistentCloudKitContainer 所需的项目设置。
  • @davidev 我遇到了与您描述的相同的问题。后台通知似乎不起作用,即使在另一台设备上对其进行了修改,列表也不会更新。请问最终解决您的问题的方法是什么?
  • 哈哈。我的谜团终于解开了。事实证明,更新时间太长了,我认为它没有更新。
【解决方案2】:

对于最近看到这个的任何人,经过大量搜索后对我的修复只是在 Persistence.swift 的 init(inMemory) 方法中添加 container.viewContext.automaticallyMergesChangesFromParent = true(所有库存都来自 Apple,用于 Xcode 12.5.1 中的 SwiftUI。一旦我添加了它并重建了 2 个模拟器,所有内容都会在 5-15 秒内同步完成。

init(inMemory: Bool = false) { 
    container = NSPersistentCloudKitContainer(name: "StoreName") 
    container.viewContext.automaticallyMergesChangesFromParent = true

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-25
    • 2020-12-20
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 2012-03-09
    • 1970-01-01
    相关资源
    最近更新 更多