【问题标题】:How to update data on cloudKit without creating a new record?如何在不创建新记录的情况下更新 cloudKit 上的数据?
【发布时间】:2018-04-15 08:29:21
【问题描述】:

这就是我想要做的。我有一个简单的日记应用程序,它有两个视图:一个 tableView 列出条目的标题,一个 viewController 有一个标题的文本字段,一个 textView 的文本正文(以及一个保存到 cloudKit 的保存按钮)。在 viewController 上,我点击保存,记录保存到 cloudKit 并成功添加到 tableView。这一切都很好。

我希望能够编辑/更新日记条目。但是当我回到日记条目,以任何方式更改它,然后再次点击保存,应用程序返回到 tableView 控制器并带有更新的条目,但是 cloudKit 创建了一个与我想要编辑的条目分开的新条目。然后,当我重新加载应用程序时,我的 fetchRecords 函数会获取 cloudKit 创建的任何额外记录。

问题:如何在 cloudKit 中编辑/更新现有日记条目而不创建新条目?

如果您需要其他内容来进一步澄清我的问题,请告诉我。 谢谢!

这是我的 cloudKit 函数:

import Foundation
import CloudKit

class CloudKitManager {

let privateDB = CKContainer.default().publicCloudDatabase //Since this is a journaling app, we'll make it private.

func fetchRecordsWith(type: String, completion: @escaping ((_ records: [CKRecord]?, _ error: Error?) -> Void)) {

    let predicate = NSPredicate(value: true) // Like saying I want everything returned to me with the recordType: type. This isn't a good idea if you have a massive app like instagram because you don't want all posts ever made to be loaded, just some from that day and from your friends or something.

    let query = CKQuery(recordType: type, predicate: predicate)

    privateDB.perform(query, inZoneWith: nil, completionHandler: completion) //Allows us to handle the completion in the EntryController to maintain proper MVC.
}

func save(records: [CKRecord], perRecordCompletion: ((_ record: CKRecord?, _ error: Error?) -> Void)?, completion: ((_ records: [CKRecord]?, _ error: Error?) -> Void)?) {
    modify(records: records, perRecordCompletion: perRecordCompletion, completion: completion )
}

func modify(records: [CKRecord], perRecordCompletion: ((_ record: CKRecord?, _ error: Error?) -> Void)?, completion: ((_ records: [CKRecord]?, _ error: Error?) -> Void)?) {

    let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)

    operation.savePolicy = .ifServerRecordUnchanged //This is what updates certain changes within a record.
    operation.queuePriority = .high
    operation.qualityOfService = .userInteractive

    operation.perRecordCompletionBlock = perRecordCompletion
    operation.modifyRecordsCompletionBlock = { (records, _, error) in
        completion?(records, error)
    }

    privateDB.add(operation) //This is what actually saves your data to the database on cloudkit. When there is an operation, you need to add it.

 }
}

这是我使用 cloudKit 功能的模型控制器:

import Foundation
import CloudKit

let entriesWereSetNotification = Notification.Name("entriesWereSet")

class EntryController {

private static let EntriesKey = "entries"

static let shared = EntryController()

let cloudKitManager = CloudKitManager()

init() {
    loadFromPersistentStorage()
}

func addEntryWith(title: String, text: String) {

    let entry = Entry(title: title, text: text)

    entries.append(entry)

    saveToPersistentStorage()
}

func remove(entry: Entry) {

    if let entryIndex = entries.index(of: entry) {
        entries.remove(at: entryIndex)
    }

    saveToPersistentStorage()
}

func update(entry: Entry, with title: String, text: String) {

    entry.title = title
    entry.text = text
    saveToPersistentStorage()
}

// MARK: Private

private func loadFromPersistentStorage() {
    cloudKitManager.fetchRecordsWith(type: Entry.TypeKey) { (records, error) in
        if let error = error {
            print(error.localizedDescription)
        }
        guard let records = records else { return } //Make sure there are records.

        let entries = records.flatMap({Entry(cloudKitRecord: $0)})
        self.entries = entries //This is connected to the private(set) property "entries"
    }
}

private func saveToPersistentStorage() {

    let entryRecords = self.entries.map({$0.cloudKitRecord})


    cloudKitManager.save(records: entryRecords, perRecordCompletion: nil) { (records, error) in
        if error != nil {
            print(error?.localizedDescription as Any)
            return
        } else {
            print("Successfully saved records to cloudKit")
        }
    }
}

// MARK: Properties

private(set) var entries = [Entry]() {

    didSet {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: entriesWereSetNotification, object: nil)
        }
    }
}
}

【问题讨论】:

  • 您是否检查过,如果您更新日记帐分录,那么recordrecordID 是否保持不变?如果recordID 相同,则CloudKit 不会创建新记录...
  • @Ladislav 我在 cloudKit 仪表板上看不到任何记录 ID。
  • 每条记录都有Record Name,这是您使用CKRecordID(recordName: "recordName")CKRecordID(recordName: "recordName", zoneID: zone) 为CloudKit 条目创建新的CKRecordID 时的名称
  • 哦,好的,我明白你在说什么。因此,当我尝试更新记录时,会创建具有新记录 ID 的新记录。我如何确保 cloudKit 知道使用特定记录 ID 的唯一更新记录?谢谢 - 我对此很陌生!
  • 在您的modify 方法中,您有这个CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil),记录是[CKRecord] 的数组,因此您要更新的每条记录确保它的record.recordID.recordName 相同而不是一个新的......

标签: swift xcode cloudkit


【解决方案1】:

这里有几个主题可能会有所帮助。

如果您在本地缓存数据,您将使用 encodesystemfields 方法创建一个新的 CKRecord,该 CKRecord 将更新服务器上现有的。

How (and when) do I use iCloud's encodeSystemFields method on CKRecord?

您似乎没有在本地缓存。我没有使用 encodesystemfields 的经验,但看起来您必须将记录拉下来并将其保存回便捷方法的完成处理程序中:

Trying to modify ckrecord in swift

【讨论】:

    猜你喜欢
    • 2010-09-17
    • 1970-01-01
    • 2019-06-04
    • 2013-04-11
    • 2019-10-01
    • 2019-12-20
    • 2011-06-04
    • 1970-01-01
    • 2017-08-01
    相关资源
    最近更新 更多