了解如何正确执行 CKQueryOperation

Understanding how to correctly execute CKQueryOperation

我是第一次使用 CloudKit,在执行 CKQueryOperation 来查询给定类型的所有记录时遇到了问题。 Apple 已经弃用了我在网上找到的大部分内容,并且除了 func 声明之外,他们对这些内容的文档完全空白,这无济于事。我想我已经完成了代码的“骨架”,但不确定 .recordMatchedBlock.queryResultsBlock.

中包含什么

我有一个 func queryAllNotes(),它应该查询 public 数据库中类型为“Notes”的所有记录和 return 笔记标题及其关联的 cloudID 的元组数组,它只是添加到数据库时赋予它的唯一记录名称。

这是 queryAllNotes() 的代码:

private func queryAllNotes() -> [(title: String, cloudID: String)] {
    /*
     query all notes in the cloud DB into an array to populate
     the tableView
     */
    var resultArray: [(title: String, cloudID: String)] = []
    
    //set the cloud database to .publicCloudDatabase
    let container = CKContainer.default()
    let cloudDB = container.publicCloudDatabase
    
    
    let pred = NSPredicate(value: true) //true -> return all records
    let query = CKQuery(recordType: "Notes", predicate: pred)
    let queryOperation = CKQueryOperation(query: query)
    queryOperation.database = cloudDB
    queryOperation.resultsLimit = 100
    
    queryOperation.recordMatchedBlock = { (record: CKRecord) in
        let noteTitle = record["Title"] as! String
        let noteCloudID = record.recordID.recordName
        
        resultArray.append((noteTitle, noteCloudID))
    }
    
    queryOperation.queryResultBlock = { (cursor, error) in
        
        
    }
    
    return resultArray
}

根据我的理解,查询 return 的每条记录都会调用 .recordMatchedBlock,所以我认为它是完整的,但我可能错了。关于 .queryResultBlock,我的理解是技术上查询一次只能 return 一条记录,这个块基本上告诉查询再次 运行 以获取所有记录的下一条记录.resultLimit。我该如何构造这个查询?我很想了解每个模块的作用。

这也是针对 macOS 应用;我不知道 macOS 和 iOS 的代码是否不同,但我想我应该包括这个以防万一。

我还收到一条错误消息,提示“没有更多上下文,表达式类型不明确”,我假设这是因为我尚未完成查询设置。如果是其他原因也可以解释为什么会这样。

编辑

我在 viewDidLoad() 中这样调用这个函数:

//array var for the array that is used to populate the tableView
var noteRecords: [(title: String, cloudID: String)] = []

override func viewDidLoad() {
    super.viewDidLoad()
    
    // do additional setup here
    
    // set serachField delegate
    searchField.delegate = self
    
    // set tableView delegate and data source
    tableView.delegate = self
    tableView.dataSource = self
    
    // load all NoteRecords in public cloud db into noteRecords
    
    noteRecords = queryAllNotes()
    
}

使用新的 async 模式,从 CloudKit 获取数据变得更加容易。

而不是 CKQueryOperation 你直接调用 records(matching:resultsLimit:) 并将结果映射到你喜欢的任何东西。

一个可能的错误交给了调用者。

func queryAllNotes() async throws -> [(title: String, cloudID: String)] {
    //set the cloud database to .publicCloudDatabase
    let container = CKContainer.default()
    let cloudDB = container.publicCloudDatabase
    
    let pred = NSPredicate(value: true) //true -> return all records
    let query = CKQuery(recordType: "Notes", predicate: pred)
    
    let (notesResults, _) = try await cloudDB.records(matching: query,
                                                        resultsLimit: 100)
    return notesResults
        .compactMap { _, result in
            guard let record = try? result.get(),
                    let noteTitle = record["Title"] as? String else { return nil }
            return (title: noteTitle, cloudID: record.recordID.recordName)
        }
}

并使用它

override func viewDidLoad() {
    super.viewDidLoad()
    
    // do additional setup here
    
    // set serachField delegate
    searchField.delegate = self
    
    // set tableView delegate and data source
    tableView.delegate = self
    tableView.dataSource = self
    
    // load all NoteRecords in public cloud db into noteRecords
    Task {
       do {
          noteRecords = try await queryAllNotes()
          tableView.reloadData()
       } catch {
          print(error)
       } 
    }
    
}

请观看 WWDC 2021 的相关视频,了解有关 async CloudKit API 以及 Apple examples on GitHub.

的详细信息

旁注:

使用结构而不是元组。不鼓励使用元组作为数据源数组。