将 CloudKit 记录保存到本地文件保存除 CKAsset 之外的所有字段

Posted

技术标签:

【中文标题】将 CloudKit 记录保存到本地文件保存除 CKAsset 之外的所有字段【英文标题】:Saving CloudKit Record to Local File Saves all fields Except CKAsset 【发布时间】:2019-01-25 04:41:00 【问题描述】:

我正在尝试将 CKRecords 数组保存到 为了快速启动和离线访问。

从 CloudKit 下载 CKRecords 工作正常,我可以在每条记录中使用 CKAsset 而不会出现问题。但是,当我将下载的 CKRecords 数组保存到本地文件时,CKAsset 不包含在数据文件中。我可以从保存到文档目录的文件大小中看出这一点。如果我将磁盘文件重组为 CKRecords 数组,我可以检索除 CKAsset 之外的所有字段。除了系统字段和 CKAsset 字段外,所有字段都是字符串。

用于测试 - 我有 10 个 CloudKit 记录,每个记录有六个小字符串字段 和一个大约 500KB 的 CKAsset。当我检查的大小 生成的文件文件大小约为 15KB。

这是保存数组的函数。 AppDelegate.ckStyleRecords 是一个 已下载 CKRecords 的静态数组。

func saveCKStyleRecordsToDisk() 

    if AppDelegate.ckStyleRecords.count != 0 

        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let docsDirectoryURL = urls[0]
        let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")

        do 
            let data : Data = try NSKeyedArchiver.archivedData(withRootObject: AppDelegate.ckStyleRecords, requiringSecureCoding: true)

            try data.write(to: ckStyleURL, options: .atomic)
            print("data write ckStyleRecords successful")

         catch 
            print("could not save ckStyleRecords to documents directory")
        

    //if count not 0

//saveCKStyleRecordsToDisk

这里是重构数组的函数。

func checkForExistenceOfCKStyleRecordsInDocuments(completion: @escaping ([CKRecord]) -> Void) 

    let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let docsDirectoryURL = urls[0]
    let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")

    var newRecords : [CKRecord] = []
    if FileManager.default.fileExists(atPath: ckStyleURL.path) 

        do 
            let data = try Data(contentsOf:ckStyleURL)

            //yes, I know this has been deprecated, but I can't seem to get the new format to work
            if let theRecords: [CKRecord] = try NSKeyedUnarchiver.unarchiveObject(with: data) as? [CKRecord] 
                        newRecords = theRecords
                        print("newRecords.count is \(newRecords.count)")
            

         catch 
            print("could not retrieve ckStyleRecords from documents directory")
        

    //if exists

    completion(newRecords)

//checkForExistenceOfckStyleRecordsInDocuments

调用上面的:

    kAppDelegate.checkForExistenceOfCKStyleRecordsInDocuments  (records) in
        print("in button press and records.count is \(records.count)")

        //this is just for test
        for record in records 
            print(record.recordID.recordName)
        

        AppDelegate.ckStyleRecords = records

    //completion block

在刷新使用 ckStyleRecords 数组的 tableView 时,所有数据 似乎是正确的,除了 CKAsset (在这种情况下是 SceneKit 场景)当然不见了。

任何指导将不胜感激。

【问题讨论】:

cloudKit 有很多延迟,当您提交请求时关闭触发,而不是在数据实际可用时触发。尝试在阅读后添加十秒的延迟 [确保],然后再保存,看看它是否能解决您的问题。如果确实是问题所在,请尝试在使用数据的资产值设置局部变量后使用 getter/setter 来触发保存。 我认为这不是问题所在。我拆分功能进行测试 - 我下载 CKRecords,将它们放在一个数组中,然后将它们显示在 tableView(包括 SCNScene)中。所以我知道我有数据。然后我使用该已知良好数组的按钮启动保存到文档目录功能。 在我从事的几乎每个项目中,我都会遇到一些 ios 框架错误。第一次发生时,我花了好几天才弄明白,也许就是这么微妙。通过代码工作,尝试只保存资产文件,忘记字符串。尝试更改用于保存它的代码。 【参考方案1】:

CKAsset 只是一个文件引用。 CKAsset 的 fileURL 属性是实际文件所在的位置。如果您保存一个 SKAsset,那么您只保存对该文件的引用。这样做时,您必须记住此 url 位于缓存位置,如果空间不足,可以清除该位置。

你可以做两件事。 1.读取备份CKAsset时,还要检查文件是否位于fileURL位置。如果该文件不存在,则从 CloudKit 再次读取它。 2. 还将文件从 fileURl 备份到您的文档文件夹。当您从备份中读取 CKAsset 时,不要从 fileURL 读取文件,而是从您将其放入文档过滤器的位置读取文件。

【讨论】:

以上是关于将 CloudKit 记录保存到本地文件保存除 CKAsset 之外的所有字段的主要内容,如果未能解决你的问题,请参考以下文章

将记录保存到 CloudKit 时应用程序崩溃

为啥 CloudKit 不允许我将任何记录保存到默认容器的公共数据库中?

将多个字段添加到 CloudKit 中的同一记录名称行

记录未保存 Cloudkit

如何将字典保存到cloudkit

如何更灵活的处理 CloudKit 从云端同步到本地的 CoreData 托管对象