Понимание того, как правильно выполнять CKQueryOperation

Я впервые работаю с CloudKit, и у меня возникают проблемы с выполнением CKQueryOperation для запроса всех записей заданного типа. Не помогает и то, что Apple объявила устаревшей большую часть вещей, которые я нашел в Интернете, и что их документация по этим вещам совершенно пуста, кроме объявления func. Я думаю, что у меня есть «скелет» кода, но я не уверен, что входит в .recordMatchedBlock и .queryResultsBlock.

У меня есть функция 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
}

Насколько я понимаю, .recordMatchedBlock вызывается для каждой записи, возвращаемой запросом, поэтому я думаю, что он завершен, но я могу сильно ошибаться. Что касается .queryResultBlock, насколько я понимаю, запрос технически возвращает только одну запись за раз, и этот блок, по сути, говорит запросу запуститься снова для следующей записи для всех записей в .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 с асинхронным API в синхронном контексте. Вы не можете использовать оба одновременно. Либо используйте новый async/await, либо добавьте обработчик завершения.

vadian 18.03.2022 16:17

@vadian Я немного запутался в том, что вы говорите, поэтому не могли бы вы проверить редактирование, где я добавил, где я звоню queryAllNotes() внутри viewDidLoad(), это мой первый раз, когда я использую cloudKit и асинхронные функции, поэтому, если бы вы могли дать ответ о том, как Структурируйте CKqueryOperation внутри func и как вызвать его внутри viewDidLoad(), это было бы очень полезно.

JonGrimes20 18.03.2022 16:46

@vadian Я удалил асинхронный тег, который, я думаю, все еще был там из предыдущей версии той функции, которую я написал, которая не работала, поэтому я думаю, что мне просто нужно правильно структурировать CKQueryOperation сейчас, я также отредактировал вопрос на отразить мой текущий код

JonGrimes20 18.03.2022 16:50

Иногда все еще может быть необходимо использовать CKQueryOperation, пока мы не получим полную замену асинхронного API, но я не вижу необходимости в чем-то большем, чем самый распространенный вариант использования. Таким образом, я действительно не думаю, что этот вопрос касается CKQueryOperation. stackoverflow.com/questions/71200053/…

Jessy 18.03.2022 17:03

@Jessy Я видел этот пост ранее и был немного сбит с толку кодом, поэтому не могли бы вы немного объяснить его, чтобы я знал, как правильно его использовать для заполнения моего массива noteRecords? Извините, если это глупый вопрос, я впервые работаю с cloudKit, и я немного растерялся.

JonGrimes20 18.03.2022 17:07

Я понятия не имею, что могло бы вас смутить, а что нет — пожалуйста, задайте любой конкретный вопрос в другом месте, а затем отправьте мне сообщение!

Jessy 18.03.2022 17:08

@Jessy Где бы я поместил код, чтобы взять индекс записи «Title» и идентификатор облака, который является просто уникальным именем записи в базе данных, и добавить их в мой массив, который возвращает func, и как бы я назвал его для заполнения моего массива noteRecords?

JonGrimes20 18.03.2022 17:11

Сделайте то, что сказал vadian, и создайте структуру. map CKRecords использует инициализатор вашей структуры. Берите оттуда то, что вам нужно.

Jessy 18.03.2022 23:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

С новым шаблоном 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 для получения подробной информации об API async CloudKit, а также Примеры Apple на GitHub.

Примечание:

Вместо кортежа используйте структуру. Кортежи в качестве массива источников данных не рекомендуются.

Сейчас я пытаюсь изменить эту функцию для работы с настраиваемыми зонами в частной базе данных, и мне было интересно, не могли бы вы взглянуть на этот вопрос здесь

JonGrimes20 29.03.2022 19:16

Вы можете создать несколько зон в частной базе данных с помощью modifyRecordZones(saving:deleting:)

vadian 29.03.2022 20:46

Я уже создал зоны в частной базе данных и поместил в них некоторые предварительно заполненные данные, чтобы проверить их, мне больше интересно, как бы я изменил эту функцию, которую вы предоставили, для работы с определенной зоной в частной базе данных. . Если я полностью пропущу отметку, я был бы более чем признателен за подробное объяснение вопроса, который я связал

JonGrimes20 29.03.2022 21:32

Прочтите документацию CKDatabase. Есть много API, которые имеют параметр зоны записи. Этот параметр можно опустить, если адресуется зона по умолчанию.

vadian 29.03.2022 21:41

Я попытался использовать .records(matching: , inZoneWith: ) , и когда я отлаживаю код, сеанс завершается, когда он достигает этой строки. Сейчас я пытаюсь использовать fetch(withQuery: , inZoneWith: ), но не знаю, как написать обработчик завершения, так как для этой функции нет документации. Если бы вы могли дать некоторое представление о том, почему функция .records не выполняется правильно, или как настроить обработчик завершения для функции .fetch, мы будем вам очень признательны.

JonGrimes20 29.03.2022 22:15

Другие вопросы по теме