3

私は、iCloud Sync に NSPersistentCloudKitContainer を使用するアプリを持っています。それを必要とするユーザーは、すべてのプラットフォーム (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 Sync を使用していない場合にのみ NSPersistentHistoryTrackingKey を設定したことが原因である可能性があることがわかりました。しかし、iCloud ユーザーに対しても NSPersistentHistoryTrackingKey を有効にすると、時計アプリが 10 個のアイテムをダウンロードするのに非常に長い時間がかかります。

Core Data は扱いにくい場合がpersistentStoreDescriptionsあり、コンテナーの属性の変更またはその他の属性は、既存のユーザーにとってアプリを壊すバグになる可能性があることを知っています。そのため、新規および既存のユーザーに役立つものが必要です。

聞く:

この問題を解決する方法を知っている人、または同様の問題を抱えている人はいますか? 私はこれを1週間近く理解しようとしてきましたが、どんな助けも大歓迎です!

4

0 に答える 0