Run CKQueryOperation with results from previous CKQueryOperation

175 views Asked by At

I have an app that is a shopping list. I can store prices per product and vendor in my app, the model is

Product
Vendor
Price

One product can have multiple prices from different vendors.

I store the price information with references to the product and vendor (CKRecord.Reference).

Now I am using the below to fetch all the prices related to a product:

public func fetchDataByProduct(product: Product, completionHandler: @escaping (Bool) -> Void){
        self.pricesBuffer = []

        let cloudContainer = CKContainer.init(identifier: "iCloud.XYZ")

        let publicDatabase = cloudContainer.publicCloudDatabase
        let reference = CKRecord.Reference(recordID: product.recordID, action: .deleteSelf)

        let predicate = NSPredicate(format: "priceToProduct == %@", reference)

        let query = CKQuery(recordType: "Price", predicate: predicate)

        let operation = CKQueryOperation(query: query)
        operation.recordFetchedBlock = { record in

            let price = Price()
            price.recordID = record.recordID

            price.grossPrice = record.object(forKey: "grossPrice") as? Double

            let dummy = record.object(forKey: "priceToVendor") as! CKRecord.Reference

            price.vendorRecordID = dummy.recordID
            self.pricesBuffer.append(price)
        }

        operation.queryCompletionBlock = { [unowned self] (cursor, error) in
            self.pricesBuffer.forEach({price in
                price.retrieveVendor()
            })

            DispatchQueue.main.async {
                if error == nil {
                    self.prices = self.pricesBuffer
                    completionHandler(true)
                } else {
                }
            }
        }
        publicDatabase.add(operation)
    }

My problem is now that I cannot retrieve the vendor name which is part of the Vendor object (Vendor.name).

I have tried to loop over the pricesBuffer and run this one per price but the problem seems to be that CloudKit first completes the initial request to fetchDataByProduct() and then afterwards fetches the vendor data but then its too late because that updated data does not get pushed to my View (SwiftUI).

publicDatabase.fetch(withRecordID: self.vendorRecordID, completionHandler:  {[unowned self] record, error in
            if let record = record {
                print(record)
                self.vendor.recordID = record.recordID
                self.vendor.name = record["name"] as! String
                print(self.vendor.name)
            }
        })

Any ideas how to solve this? I believe I have to add a second CKQueryOperation to the mix and use the .addDependency() but I cannot wrap my head around how that should look like in the end.

1

There are 1 answers

0
Stamenkovski On

Let's say you use the operation to fetch prices like above.

let predicate = NSPredicate(format: "priceToProduct == %@", reference)

let query = CKQuery(recordType: "Price", predicate: predicate)

let pricesOperation = CKQueryOperation(query: query)
pricesOperation.database = publicDatabase // not required if you don't use OperationQueue

Then you can construct operation to fetch vendors, I will create simple operation for demo purposes.

let vendorQuery = CKQuery(recordType: "Vendor", predicate: predicate)
    
let vendorsOperation = CKQueryOperation(query: vendorQuery)
vendorsOperation.database = publicDatabase // not required if you don't use OperationQueue

Then you can set dependency, first fetch prices than vendors.

vendorsOperation.addDependency(pricesOperation)

And lastly submit those operations to OperationQueue

let operationQueue = OperationQueue()
        
operationQueue.addOperations([pricesOperation, vendorsOperation], waitUntilFinished: false)

Edit: If you don't want to use OperationQueue, simply submit those operations to database, but first set dependency before submitting the operations to be executed.

vendorsOperation.addDependency(pricesOperation)
publicDatabase.add(pricesOperation)
publicDatabase.add(vendorsOperation)