CloudKit – CKQueryOperation с зависимостью

Я только начинаю работать с CloudKit, так что медведь со мной.

Справочная информация

На WWDC 2015 яблоко рассказало о CloudKit https://developer.apple.com/videos/wwdc/2015/?id=715

В этом разговоре они предупреждают о создании цепочки запросов и вместо этого рекомендуют эту тактику:

let firstFetch = CKFetchRecordsOperation(...) let secondFetch = CKFetchRecordsOperation(...) ... secondFetch.addDependency(firstFetch) letQueue = NSOperationQueue() queue.addOperations([firstFetch, secondFetch], waitUntilFinished: false) 

Пример структуры

База данных тестового проекта содержит домашних животных и их владельцев, это выглядит так:

 |Pets | |Owners | |-name | |-firstName | |-birthdate | |-lastName | |-owner (Reference) | | | 

Мой вопрос

Я пытаюсь найти всех домашних животных, принадлежащих владельцу, и я обеспокоен тем, что я создаю цепочку apple, предупреждающую об этом. См. Ниже два метода, которые делают то же самое, но два пути. Что является более правильным или ошибочным? Я чувствую, что делаю то же самое, но вместо этого использую только блоки завершения.

Я смущен тем, как изменить otherSearchBtnClick: использовать зависимость. Где мне нужно добавить

 ownerQueryOp.addDependency(queryOp) 

в otherSearchBtnЩелкните 😕

 @IBAction func searchBtnClick(sender: AnyObject) { var petString = "" let container = CKContainer.defaultContainer() let publicDatabase = container.publicCloudDatabase let privateDatabase = container.privateCloudDatabase let predicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'") let ckQuery = CKQuery(recordType: "Owner", predicate: predicate) publicDatabase.performQuery(ckQuery, inZoneWithID: nil) { record, error in if error != nil { println(error.localizedDescription) } else { if record != nil { for owner in record { let myRecord = owner as! CKRecord let myReference = CKReference(record: myRecord, action: CKReferenceAction.None) let myPredicate = NSPredicate(format: "owner == %@", myReference) let petQuery = CKQuery(recordType: "Pet", predicate: myPredicate) publicDatabase.performQuery(petQuery, inZoneWithID: nil) { record, error in if error != nil { println(error.localizedDescription) } else { if record != nil { for pet in record { println(pet.objectForKey("name") as! String) } } } } } } } } } @IBAction func otherSearchBtnClick (sender: AnyObject) { let container = CKContainer.defaultContainer() let publicDatabase = container.publicCloudDatabase let privateDatabase = container.privateCloudDatabase let queue = NSOperationQueue() let petPredicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'") let petQuery = CKQuery(recordType: "Owner", predicate: petPredicate) let queryOp = CKQueryOperation(query: petQuery) queryOp.recordFetchedBlock = { (record: CKRecord!) in println("recordFetchedBlock: \(record)") self.matchingOwners.append(record) } queryOp.queryCompletionBlock = { (cursor: CKQueryCursor!, error: NSError!) in if error != nil { println(error.localizedDescription) } else { println("queryCompletionBlock: \(cursor)") println("ALL RECORDS ARE: \(self.matchingOwners)") for owner in self.matchingOwners { let ownerReference = CKReference(record: owner, action: CKReferenceAction.None) let ownerPredicate = NSPredicate(format: "owner == %@", ownerReference) let ownerQuery = CKQuery(recordType: "Pet", predicate: ownerPredicate) let ownerQueryOp = CKQueryOperation(query: ownerQuery) ownerQueryOp.recordFetchedBlock = { (record: CKRecord!) in println("recordFetchedBlock (pet values): \(record)") self.matchingPets.append(record) } ownerQueryOp.queryCompletionBlock = { (cursor: CKQueryCursor!, error: NSError!) in if error != nil { println(error.localizedDescription) } else { println("queryCompletionBlock (pet values)") for pet in self.matchingPets { println(pet.objectForKey("name") as! String) } } } publicDatabase.addOperation(ownerQueryOp) } } } publicDatabase.addOperation(queryOp) } 

3 Solutions collect form web for “CloudKit – CKQueryOperation с зависимостью”

в теории вы могли бы иметь несколько владельцев и, следовательно, несколько зависимостей. Кроме того, внутренние запросы будут созданы после выполнения внешнего запроса. Вы опоздаете, чтобы создать зависимость. В вашем случае, вероятно, проще принудительно выполнить внутренние запросы в отдельной очереди следующим образом:

 if record != nil { for owner in record { NSOperationQueue.mainQueue().addOperationWithBlock { 

Таким образом, вы убедитесь, что каждый внутренний запрос будет выполнен в новой очереди и в то же время, когда родительский запрос может завершиться.

Что-то еще: чтобы сделать ваш код более чистым, было бы лучше, если бы весь код внутри цикла for был в отдельной функции с CKReference в качестве параметра.

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

Я знаю, что знаю, в WWDC 2015 Нихар Шарма рекомендовал подход зависимости, но, похоже, он просто бросил это в конце, не задумываясь. Вы видите, что повторить NSOperation невозможно, потому что они все равно одноразовые, и он не предложил никакого примера для отмены операций уже в очереди или как передать данные из одной операции из следующей. Учитывая эти 3 осложнения, которые могут занять у вас несколько недель, просто придерживайтесь того, что у вас есть, и ждите следующего WWDC для их решения. Плюс весь блок блоков позволяет вам вызывать встроенные методы и иметь доступ к параметрам в вышеописанном методе, поэтому, если вы перейдете к операциям, вы, как правило, не получаете полного преимущества этой выгоды.

Его главная причина не использовать цепочку – это смешно, что он не мог сказать, какая ошибка для какого запроса, он называет свои ошибки someError, а затем otherError и т. Д. Никто в их правой голове не ошибается, если параметры params различны внутри блоков, поэтому просто используйте одинаковое имя для всех из них, а затем вы знаете, что внутри блока вы всегда используете правильную ошибку. Таким образом, он был тем, кто создал свой беспорядочный сценарий и предложил решение для него, однако лучшее решение просто не создает беспорядочный сценарий множественных имен параметров ошибок в первую очередь!

При всем том, что вы говорите, в случае, если вы все еще хотите попытаться использовать зависимости от работы, вот пример того, как это можно сделать:

 __block CKRecord* venueRecord; CKRecordID* venueRecordID = [[CKRecordID alloc] initWithRecordName:@"4c31ee5416adc9282343c19c"]; CKFetchRecordsOperation* fetchVenue = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[venueRecordID]]; fetchVenue.database = [CKContainer defaultContainer].publicCloudDatabase; // init a fetch for the category, it's just a placeholder just now to go in the operation queue and will be configured once we have the venue. CKFetchRecordsOperation* fetchCategory = [[CKFetchRecordsOperation alloc] init]; [fetchVenue setFetchRecordsCompletionBlock:^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) { venueRecord = recordsByRecordID.allValues.firstObject; CKReference* ref = [venueRecord valueForKey:@"category"]; // configure the category fetch fetchCategory.recordIDs = @[ref.recordID]; fetchCategory.database = [CKContainer defaultContainer].publicCloudDatabase; }]; [fetchCategory setFetchRecordsCompletionBlock:^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) { CKRecord* categoryRecord = recordsByRecordID.allValues.firstObject; // here we have a venue and a category so we could call a completion handler with both. }]; NSOperationQueue* queue = [[NSOperationQueue alloc] init]; [fetchCategory addDependency:fetchVenue]; [queue addOperations:@[fetchVenue, fetchCategory] waitUntilFinished:NO]; 

Как это работает, сначала он записывает запись о месте, затем выбирает ее категорию.

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

У меня была та же проблема в последнее время и в итоге я использовал NSBlockOperation для подготовки второго запроса и добавил зависимость, чтобы заставить все это работать:

  let container = CKContainer.defaultContainer() let publicDB = container.publicCloudDatabase let operationqueue = NSOperationQueue.mainQueue() let familyPredicate = NSPredicate(format: "name == %@", argumentArray: [familyName]) let familyQuery = CKQuery(recordType: "Familias", predicate: familyPredicate) let fetchFamilyRecordOp = CKQueryOperation(query: familyQuery) fetchFamilyRecordOp.recordFetchedBlock = { record in familyRecord = record } let fetchMembersOP = CKQueryOperation() // Once we have the familyRecord, we prepare the PersonsFetch let prepareFamilyRef = NSBlockOperation() { let familyRef = CKReference(record: familyRecord!, action: CKReferenceAction.None) let familyRecordID = familyRef?.recordID let membersPredicate = NSPredicate(format: "familia == %@", argumentArray: [familyRecordID!]) let membersQuery = CKQuery(recordType: "Personas", predicate: membersPredicate) fetchMembersOP.query = membersQuery } prepareFamilyRef.addDependency(fetchFamilyRecordOp) fetchMembersOP.recordFetchedBlock = { record in members.append(record) } fetchMembersOP.addDependency(prepareFamilyRef) fetchMembersOP.database = publicDB fetchFamilyRecordOp.database = publicDB operationqueue.addOperations([fetchFamilyRecordOp, fetchMembersOP, prepareFamilyRef], waitUntilFinished: false) 

И теперь он работает так, как я ожидал, потому что вы можете настроить свои операции очень гранулированным образом и выполнить их в правильном порядке ^. ^

в вашем случае я бы структурировал это следующим образом:

 let predicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'") let ckQuery = CKQuery(recordType: "Owner", predicate: predicate) let getOwnerOperation = CKQueryOperation(query: ckQuery) getOwnerOperation.recordFetchedBlock = { record in let name = record.valueForKey("name") as! String if name == myOwnerName { ownerRecord = record } } //now we have and operation that will save in our var OwnerRecord the record that is exactly our owner //now we create another that will fetch our pets let queryPetsForOurOwner = CKQueryOperation() queryPetsForOurOwner.recordFetchedBlock = { record in results.append(record) } //That's all this op has to do, BUT it needs the owner operation to be completed first, but not inmediately, we need to prepare it's query first so: var fetchPetsQuery : CKQuery? let preparePetsForOwnerQuery = NSBlockOperation() { let myOwnerRecord = ownerRecord! let ownerRef = CKReference(record: myOwnerRecord, action: CKReferenceAction.None) let myPredicate = NSPredicate(format: "owner == %@", myReference) fetchPetsQuery = CKQuery(recordType: "Pet", predicate: myPredicate) } queryPetsForOurOwner.query = fetchPetsQuery preparePetsForOwnerQuery.addDependency(getOwnerOperation) queryPetsForOurOwner.addDependency(preparePetsForOwnerQuery) 

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

 getOwnerOperation.database = publicDB queryPetsForOurOwner.database = publicDB let operationqueue = NSOperationQueue.mainQueue() operationqueue.addOperations([getOwnerOperation, queryPetsForOurOwner, preparePetsForOwnerQuery], waitUntilFinished: false) 

PS: Я знаю, что я сказал «Семья и Личность», и имена не такие, но я испанский и тестирую некоторые операции с облачным типом, поэтому я еще не стандартизовал имена имен на русском языке;)

  • iOS, ld: рамки не найдены GoogleMaps для архитектуры arm64
  • Получение ошибки в классе FBSDKApplicationDelegate
  • Представление контроллера вида с тусклым фоном
  • Тип 'URL' не имеет имени пользователя fileURL '- Swift 3
  • UICollectionViewCell с AutoLayout, не работающим в iOS 10
  • страницы настроек для добавления или вычитания различных массивов изображений. Кодирование с быстрым и xcode
  • Как разобрать строку в NSTimeInterval
  • Устранение отличительного размытия между UINavigationBar и UIVisualEffectView
  • «Использование неразрешенного идентификатора» в Swift
  • Как передавать данные в условном режиме разговора?
  • Передача данных в и из Popover в Swift
  • Interesting Posts

    AVAudioPlayer выдает точку останова в режиме отладки

    iOS – изменение ограничений привязки программно

    CoreData меняет отношения в цикле

    Как я могу использовать JavaScript для установки уровня масштабирования на мобильном сафари?

    Использование Apple Script для перемещения нескольких файлов из нескольких папок в один

    AVAudioRecorder не создает файлы .m4a, которые можно открыть и воспроизвести

    Как добавить UITabBarController программно (нет xib-файла или раскадровки)

    Как транслировать видео в реальном времени с iPhone

    StoreKit и LinkShare

    Как передать значения из контроллера Pop Up View в контроллер предыдущего просмотра?

    Circular ImageView iOS 8 / Xcode 6

    Установка старой версии через TestFlight

    gcm push-уведомление: первый успех, затем не зарегистрированный в IOS

    iOS не появляется для тестирования бета-тестирования testflight

    Нуль Нуль Нуль Нуль Нуль Нуль Нуль Нульвиун Нуль Нуль Нуль Нуль Нуль Нуль Нуль Нужно

    PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.