正确使用 CloudKit 容器

问题描述

我有一个应用程序,它使用 NSPersistentCloudKitContainer 将 iCloud 同步到需要它的用户,并且适用于所有平台(iPhone/iPad/Mac)。但是现在,在尝试添加 Apple Watch 支持时,我意识到我可能一直以来都错误地实施了 CloudKit。

代码

final class CoreDataManager {
    static let sharedManager = CoreDataManager()
    
    private var observer: NSkeyvalueObservation?
    lazy var persistentContainer: NSPersistentContainer = {
        setupContainer()
    }()
    
    private func setupContainer() -> NSPersistentContainer {
        var useCloudSync = UserDefaults.standard.bool(forKey: "useCloudSync")
        #if os(watchOS)
        useCloudSync = true
        #endif

        let containerToUse: NSPersistentContainer?
        if useCloudSync {
             containerToUse = NSPersistentCloudKitContainer(name: "app")
        } else {
             containerToUse = NSPersistentContainer(name: "app")
        }

        guard let container = containerToUse,let description = container.persistentStoreDescriptions.first else {
             fatalError("Could not get a container!")
        }
        description.setoption(true as NSNumber,forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)

        if !useCloudSync {
             description.setoption(true as NSNumber,forKey: NSPersistentHistoryTrackingKey)
        }

        container.loadPersistentStores(completionHandler: { (storeDescription,error) in }

        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.transactionAuthor = "app"
        container.viewContext.automaticallyMergesChangesFromParent = true        
        
        NotificationCenter.default.addobserver(self,selector: #selector(type(of: self).storeRemoteChange(_:)),name: .NSPersistentStoreRemoteChange,object: container.persistentStoreCoordinator)
        
        return container
    }
}


//MARK: - History token

private lazy var historyQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 1
    return queue
}()
    

private var lastHistoryToken: NSPersistentHistoryToken? = nil {
    didSet {
        guard let token = lastHistoryToken,let data = try? NSKeyedArchiver.archivedData(withRootObject: token,requiringSecureCoding: true) else { return }
               
        do {
            try data.write(to: tokenFile)
        } catch {
            print("###\(#function): Failed to write token data. Error = \(error)")
        }
    }
}


private lazy var tokenFile: URL = {
    let url = NSPersistentCloudKitContainer.defaultDirectoryURL().appendingPathComponent("app",isDirectory: true)

    if !FileManager.default.fileExists(atPath: url.path) {
        do {
            try FileManager.default.createDirectory(at: URL,withIntermediateDirectories: true,attributes: nil)
        } catch {
            print("###\(#function): Failed to create persistent container URL. Error = \(error)")
        }
    }
    return url.appendingPathComponent("token.data",isDirectory: false)
}()


init() {
    // Load the last token from the token file.
    if let tokenData = try? Data(contentsOf: tokenFile) {
        do {
            lastHistoryToken = try NSKeyedUnarchiver.unarchivedobject(ofClass: NSPersistentHistoryToken.self,from: tokenData)
        } catch {
            print("###\(#function): Failed to unarchive NSPersistentHistoryToken. Error = \(error)")
        }
    }
}


//MARK: - Process History

@objc func storeRemoteChange(_ notification: Notification) {
    // Process persistent history to merge changes from other coordinators.
    historyQueue.addOperation {
        self.processpersistentHistory()
    }
}

func processpersistentHistory() {

    let backContext = persistentContainer.newBackgroundContext()
    backContext.performAndWait {
        let request = NSPersistentHistoryChangeRequest.fetchHistory(after: lastHistoryToken)

        let result = (try? backContext.execute(request)) as? NSPersistentHistoryResult
        guard let transactions = result?.result as? [NSPersistentHistoryTransaction],!transactions.isEmpty else {
            print("No transactions from persistent history")
            return
        }

        // Update the history token using the last transaction.
        if let lastToken = transactions.last?.token {
            lastHistoryToken = lastToken
        }
    }
}

我注意到的:

我的测试设备上只有 10 个项目。当我启动手表应用程序并查看控制台时,它似乎正在浏览我所做过的每次添加删除项目的整个历史记录,这使得下载我实际拥有的 10 个项目需要很长时间离开了。

我查看了我的 iCloud 存储,发现当 10 个项目只是附加了一些字符串的实体时,我的应用程序占用了大量空间(48 MB)

研究:

我做了很多研究,发现只有在用户不使用 iCloud 同步时才设置 NSPersistentHistoryTrackingKey。但是当我也为 iCloud 用户启用 NSPersistentHistoryTrackingKey 时,手表应用仍然需要很长时间才能下载 10 个项目。

我知道 Core Data 可能很棘手,并且更改容器的 persistentStoreDescriptions 或其他属性对于现有用户来说可能是一个破坏应用程序的错误。所以我需要一些对新用户和现有用户都适用的东西。

询问:

有谁知道如何解决这个问题或遇到过类似的问题?我已经尝试解决这个问题将近一个星期了,任何帮助将不胜感激!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)