繁体   English   中英

应用程序卸载/重新安装后的 CoreData + Cloudkit fetch() 不返回任何结果

[英]CoreData + Cloudkit fetch() after app uninstall/reinstall returns no results

我正在尝试使用NSPersistentCloudKitContainer配置 CoreData+CloudKit 以自动将数据同步到/从 CloudKit。

按照Apple 指南,设置实体、在 Xcode 中添加适当的功能、在我的 AppDelegate 中设置persistentContainersaveContext

我正在通过NSManagedObjectContext进行fetch()save()调用,并且我能够毫无问题地保存和获取记录。 我可以在 CloudKit 仪表板中看到它们。

但是,如果我从模拟器卸载应用程序并重建/重新安装,我的fetchRequest (没有NSPredicate或排序,只是获取所有记录)总是返回一个空列表。 我使用的是同一个 iCloud 帐户,并且尝试了公共和私有数据库范围。 如果我创建一个新记录然后重试我的提取请求,我可以检索该新创建的记录,但不能检索任何旧记录。 我 100% 确定这些记录仍在 CloudKit 数据库中,因为我可以在 CloudKit Dashboard Web 应用程序上看到它们。

我查看了Apple 的 CoreDataCloudKitDemo应用程序,它能够在卸载/重新安装后从 CloudKit 数据库中获取“发布”实体,所以我知道这是可能的。 但是,它使用的是NSFetchedResultsController ,这不适用于我的应用程序(我的是 SpriteKit 游戏)。

我尝试将我的 CoreData+Cloudkit 代码复制到一个全新的 Xcode 项目中,我可以在那里重现这个问题。 这是我的代码供参考:

import UIKit
import CoreData

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    lazy var persistentContainer: NSPersistentContainer = {

        // Create a container that can load CloudKit-backed stores
        let container = NSPersistentCloudKitContainer(name: "coredatacloudkitexample")

        // Enable history tracking and remote notifications
        guard let description = container.persistentStoreDescriptions.first else {
            fatalError("###\(#function): Failed to retrieve a persistent store description.")
        }
        description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        description.cloudKitContainerOptions?.databaseScope = .public

        container.loadPersistentStores(completionHandler: { (_, error) in
            guard let error = error as NSError? else { return }
            fatalError("###\(#function): Failed to load persistent stores:\(error)")
        })

        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.transactionAuthor = "nibbler"

        // Pin the viewContext to the current generation token and set it to keep itself up to date with local changes.
        container.viewContext.automaticallyMergesChangesFromParent = true
        do {
            try container.viewContext.setQueryGenerationFrom(.current)
        } catch {
            fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
        }

        return container
    }()
}


// ------

import UIKit
import CoreData

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
        let people: [Person]

        do {
            people = try viewContext.fetch(fetchRequest)
            print("---> fetched People from CoreData: \(people)")

            // people.isEmpty is ALWAYS true (empty array) on first install of app, even if records exist in table in CloudKit
            if people.isEmpty {
                let person = Person(context: viewContext)
                person.name = "nibbler"

                // save the data through managed object context
                do {
                    try viewContext.save()
                    print("--> created Person in CoreData: \(person)")
                } catch {
                    print("---> failed to save Person: \(error.localizedDescription)")
                }
            }
        } catch {
            print("---> error: \(error)")
        }
    }
}


我错过了什么? 为什么我只能获取在此应用程序安装期间创建的记录而不是之前的记录?

更新:看来,如果我等了几秒钟,然后重新尝试我取的第一个应用程序安装,能够检索从CloudKit数据库的结果。 首次启动时,我还可以在控制台中看到大量CoreData+CloudKit日志消息。 这就是我的想法——即使在使用NSPersistentCloudKitContainerfetch()正在读取/写入本地 CoreData 存储,然后在后台运行一个单独的进程来镜像和合并本地 CoreData 记录与 CloudKit 记录。

因此,我相信我需要以某种方式等待/被通知本地 CoreData 和 CloudKit 记录的同步/合并在首次启动时已完成,然后再进行fetch()调用,而不是在应用程序打开时立即进行fetch()调用. 有任何想法吗?

您需要使用NSFetchedResultsController ,为什么您认为它不适用于您的应用程序?

它之所以必要是NSFetchedResultsController监视viewContext和当同步进程的下载的新对象,将它们插入到一个背景历境的automaticallyMergesChangesFromParent合并到对象viewContext并前进生成令牌。 FRC 的委托方法被调用以通知您是否从获取的对象数组中插入、更新或删除对象,这些对象是上下文中与获取请求实体和谓词匹配的对象。

你可以尝试的是:

  • 设置NSPersistentCloudKitContainerOptions
let id = "iCloud.yourid"  
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)  
description?.cloudKitContainerOptions = options
  • 初始化您的 CloudKit 架构(至少需要一次)
do {
     try container.initializeCloudKitSchema()
  } catch {
    print("ERROR \(error)")
 }

编辑:
你能改变lazy var persistentContainer: NSPersistentContainer

lazy var persistentContainer: NSPersistentCloudKitContainer

这是你在模拟器上删除应用程序时提到的@professormeowingtons,它不会显示以前的记录,所以我的建议是在已经配置了 iCloud 帐户的真实设备上试用你的应用程序,这样你就会能够向您的数据库添加一些记录,然后删除该应用程序,重新安装并获取您之前输入的所有记录。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM