3

我有一个应用程序,它使用 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 存储,发现我的应用程序占用了大量空间 (48 MB),而这 10 个项目只是附有一些字符串的实体

研究:

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

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

问:

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

4

0 回答 0