簡體   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