Search code examples
swiftuiscrollviewcloudkitasyncdisplaykitbatch-fetching

batch fetching with async display kit / cloudkit


I am trying to implement batch fetching to integrate with cloudkit and async display kit. However, when I try to scroll around, I get this error:

[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued'

Any idea would this be happening? Thanks!

These are my networking calls:

var thisCursor : CKQueryCursor?
var newQueryOP : CKQueryOperation?

func pullPosts(curLocation: CLLocation, completionHandler: @escaping(([PostMap]) -> Bool)) {
    print("------------PULLING POSTS (1ST PULL) ----------------")
    let location = curLocation

    var annotations : [PostMap] = [PostMap]()

    //let curLocation = mapLocationManager.getCurrentLocation()
    let locationPredicate = NSPredicate(format: "distanceToLocation:fromLocation:(%K, %@) < %f", "Location", location,
                                        CKConstants.loadPinsRadius)
    let query = CKQuery(recordType: "PostMap", predicate: locationPredicate)
    query.sortDescriptors = [CKLocationSortDescriptor(key: "Location", relativeLocation: location)]
    let queryOP = CKQueryOperation(query: query)

    print("SERVER DECIDED QUERY LIMIT  \(queryOP.resultsLimit)")

    queryOP.recordFetchedBlock = { record in
        //MAKE A NEW POST (taken out for brevity)
        annotations.append(postToAdd)
    }

    queryOP.queryCompletionBlock = { [unowned self] (cursor, error) in
        DispatchQueue.main.async {
            if error == nil {
                if completionHandler(annotations) {
                    if cursor != nil {
                        print("THIS CURSOR IS NOT NIL")
                        self.thisCursor = cursor
                        self.newQueryOP = CKQueryOperation(cursor: cursor!)
                    }
                }
            } else {
                print(error)
                print("could not pull posts")
            }
        }
    }

    queryOP.resultsLimit = 15
    CKContainer.default().publicCloudDatabase.add(queryOP)
}

public func continuePullPosts(curLocation: CLLocation, queryOP: CKQueryOperation,
                               annotations: [PostMap], completionHandler: @escaping(([PostMap]) -> Bool)) {
    var annotations = annotations
    print("------------CONTINUE PULLING POSTS ----------------")
    queryOP.recordFetchedBlock = { record in
        //MAKE A NEW POST (taken out for brevity)
        annotations.append(postToAdd)
    }

    queryOP.queryCompletionBlock = { [unowned self] (cursor, error) in
        DispatchQueue.main.async {
            if error == nil {
                if completionHandler(annotations) {
                    if cursor != nil {
                        print("paging posts")
                        self.thisCursor = cursor!
                        self.newQueryOP = CKQueryOperation(cursor: cursor!)
                    }
                }
            } else {
                print(error)
                print("could not pull posts")
            }
        }
    }

    queryOP.resultsLimit = CKConstants.subsequentPullAmount
    CKContainer.default().publicCloudDatabase.add(queryOP)
}

In my TableViewController:

func shouldBatchFetch(for tableNode: ASTableNode) -> Bool {
    if (pinDataManager.thisCursor == nil) {
        return false
    }; return true
}

func tableNode(_ tableNode: ASTableNode, willBeginBatchFetchWith context: ASBatchContext) {
    var annotations : [PostMap] = [PostMap]()
    pinDataManager.continuePullPosts(curLocation: locationManager.getCurrentLocation(), queryOP: pinDataManager.newQueryOP!, annotations: annotations) { (annotations) -> Bool in
        self.insertNewRowsInTableNode(annotations: annotations)
        context.completeBatchFetching(true)
        return true
    }
}

func insertNewRowsInTableNode(annotations: [MapObject]) {
    let section : NSInteger = 0
    let indexPaths = NSMutableArray()
    let totalPosts = self.postObjects.count + annotations.count
    print(self.postObjects.count)
    print(annotations.count)

    for index in self.postObjects.count...totalPosts - 1 {
        let path = NSIndexPath(row: index, section: section)
        indexPaths.add(path)
    }
    self.postObjects = self.postObjects + annotations
    self.tableNode.insertRows(at: indexPaths as! [IndexPath], with: .none)
}

Solution

  • You're passing a completed operation into your continuePullPosts function, then attempting to re-add it to the database.

    Instead, you need to save the cursor returned from the first operation, pass that cursor to continuePullPosts and then create a new follow-up operation using initWithCursor.

    You need to continue to create new operations with initWithCursor every time you receive a cursor until the query eventually finishes and returns a nil cursor.