简体   繁体   English

了解如何正确执行 CKQueryOperation

[英]Understanding how to correctly execute CKQueryOperation

I'm working with CloudKit for the first time and am having trouble executing a CKQueryOperation to query all records of a given type.我是第一次使用 CloudKit,在执行 CKQueryOperation 来查询给定类型的所有记录时遇到了问题。 It doesn't help that Apple has deprecated most of the stuff I've found online and that their documentation for these things are completely blank besides the func declaration. Apple 已经弃用了我在网上找到的大部分内容,并且除了 func 声明之外,他们对这些内容的文档完全空白,这无济于事。 I think I've got the "skeleton" of the code done but am unsure of what goes into the .recordMatchedBlock and the .queryResultsBlock .我想我已经完成了代码的“骨架”,但不确定.recordMatchedBlock.queryResultsBlock中包含什么。

I have a func queryAllNotes() which should query all records in the public database of type "Notes" and return an array of tuples of the note's title and its associated cloudID, which is just the unique recordName given to it when it is added to the database.我有一个 func queryAllNotes() 应该查询类型为“Notes”的公共数据库中的所有记录,并返回笔记标题及其关联的 cloudID 的元组数组,这只是在添加到它时赋予它的唯一 recordName数据库。

Here's the code for queryAllNotes():这是 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
}

To my understanding the .recordMatchedBlock is called for every record returned by the query so I think it is complete but I could be very wrong.据我了解,查询返回的每条记录都会调用.recordMatchedBlock ,因此我认为它是完整的,但我可能错了。 In regards to the .queryResultBlock , my understanding is that the query technically only return one record at a time and this block basically tells the query to run again for the next record for all records within the .resultLimit .关于.queryResultBlock ,我的理解是查询在技术上一次只返回一条记录,这个块基本上告诉查询再次运行以.resultLimit中所有记录的下一条记录。 How can I structure this query?我该如何构造这个查询? I am keen to understand what each of these blocks do.我很想了解每个模块的作用。

Also this is for a macOS app;这也适用于 macOS 应用程序; I don't know if the code is different for macOS vs iOS but I thought I should include this just in case.我不知道 macOS 和 iOS 的代码是否不同,但我想我应该包括这个以防万一。

Also I'm getting an error saying "Type of expression is ambiguous without more context" which I'm assuming is because I haven't completed setting up my query.我还收到一条错误消息,说“没有更多上下文,表达式类型不明确”,我假设这是因为我还没有完成查询设置。 If it's for a different reason could also explain why this is happening.如果是出于其他原因,也可以解释为什么会这样。

Edit编辑

I call this func inside of viewDidLoad() like so:我在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()
    
}

With the new async pattern it has become much easier to fetch data from CloudKit.使用新的async模式,从 CloudKit 获取数据变得更加容易。

Instead of CKQueryOperation you call records(matching:resultsLimit:) directly and map the result to whatever you like.而不是CKQueryOperation你直接调用records(matching:resultsLimit:)和 map 结果到你喜欢的任何东西。

A possible error is handed over to the caller.一个可能的错误被移交给调用者。

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)
        }
}

And use it并使用它

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)
       } 
    }
    
}

Please watch the related video from WWDC 2021 for detailed information about the async CloudKit APIs and also the Apple examples on GitHub .请观看 WWDC 2021 的相关视频,了解有关async CloudKit API 的详细信息以及 GitHub 上的 Apple 示例

Side note:边注:

Rather than a tuple use a struct.而不是元组使用结构。 Tuples as data source array are discouraged.不鼓励使用元组作为数据源数组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM