ios - CloudKit - 具有依赖性的 CKQueryOperation

标签 ios swift cloudkit

我才刚刚开始使用 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) |   |           |

我的问题

我正在尝试查找属于某个所有者的所有宠物,但我担心我正在创建链苹果警告。请参阅下面的两种方法,它们做同样的事情,但有两种方式。哪个更正确或者都错了?我觉得我在做同样的事情,但只是使用了完成 block 。

我对如何更改 otherSearchBtnClick: 以使用依赖项感到困惑。我需要在哪里添加

ownerQueryOp.addDependency(queryOp)

在 otherSearchBtnClick:?

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

最佳答案

如果您不需要取消并且不为出现网络错误而重试而烦恼,那么我认为您可以很好地链接查询。

我知道我知道,在 WWDC 2015 中,Nihar Sharma 推荐了添加依赖项的方法,但看起来他只是在最后没有多想就把它扔进去了。您会看到不可能重试 NSOperation,因为它们无论如何都是一次性的,并且他没有提供取消队列中已有操作的示例,或者如何从一个操作传递数据到下一个操作。鉴于这 3 个复杂问题可能需要您数周才能解决,请坚持您的工作并等待下一次 WWDC 的解决方案。此外, block 的全部意义在于让您调用内联方法并能够访问上述方法中的参数,因此如果您转向操作,您可能无法充分利用该优势。

他不使用链接的主要原因是一个荒谬的原因,他无法分辨哪个错误是针对哪个请求,他将他的错误命名为 someError 然后是 otherError 等等。没有人在他们的正确思想中命名错误参数不同的内部 block 所以只需对所有这些使用相同的名称,然后您就会知道在一个 block 中您始终使用正确的错误。因此,他是创建他的困惑场景并为其提供解决方案的人,但是最好的解决方案就是不要首先创建多个错误参数名称的困惑场景!

话虽如此,如果您仍然想尝试使用操作依赖项,这里有一个如何完成的示例:

__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];

它的工作原理是首先获取一个 Venue 记录,然后获取它的类别。

抱歉,没有错误处理,但如您所见,已经有大量代码来做一些可以通过链接在几行中完成的事情。我个人觉得这个结果比简单地将便利方法链接在一起更令人费解和困惑。

关于ios - CloudKit - 具有依赖性的 CKQueryOperation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32355278/

相关文章:

ios - Swift 3 管理部分标题的标题

swift - 来自 Firebase 网站的 Firebase 存储错误

ios - CloudKit "aggregate"是否更新更改通知?

ios - CloudKit 不检索应该存在且可检索的记录

ios - 类型 'NSNotification.Name' 没有成员 'UITextField'

ios - 使用适用于 iOS 的 XMPP 框架 : Receiving presence when a subscriber becomes available/unavailable

ios - Swift 3 转换 "has no member"错误

iphone - 如何强制MPMoviePlayerViewController全屏显示?

cloudkit - 使用 CloudKit 访问 iCloud 笔记

ios - 我在 Swift 应用程序中的哪里存储文件选项?