-1

I am building an application which uses CloudKit as backend. I have a ShoppingList record and GroceryItem record. A shopping list can have many grocery items. I am using the reverse relationship technique which means grocery items have a reference to the shopping list (parent).

Now, I want to display the number of grocery items in the shopping list. Here is my implementation:

-(void) getAllShoppingLists:(GetAllShoppingListsResult)getAllShoppingLists {

    CKQuery *query = [[CKQuery alloc] initWithRecordType:@"ShoppingLists" predicate:[NSPredicate predicateWithValue:YES]];

    [_privateDB performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {

        for(CKRecord *record in results) {

            ShoppingList *shoppingList = [[ShoppingList alloc] initWithRecord:record];
            [self getItemsByShoppingList:shoppingList result:^(NSArray *results, NSError *error) {

                shoppingList.noOfGroceryItems = results.count;

            }];

        }

        **getAllShoppingLists(results,error);** THIS RETURNS WITHOUT THE noOfGroceryItems BEING UPDATED

    }];
}

What can I do to solve this issue? Is there a better way using CKQuery to simple get the total number of grocery items instead of running a for each on each single Shopping List?

john doe
  • 8,162
  • 20
  • 71
  • 141
  • Asynchronous methods are best handled with callbacks, such as completion blocks. Change your method to getAllShoppingLists:withCompletion:, and call the completion when the lists are available. Update your UI in the completion block. – Drew McCormack Jan 27 '15 at 09:39
  • The main issue is that I still need to get the no of grocery items in each shopping list. Can you provide rough implementation of what you mean? Also, I do invoke a completion block getAllShoppingList in the end is a completion block. – john doe Jan 27 '15 at 18:09
  • Ah, I see what you mean now. How I would handle it is gather all shopping lists in an array, and use a predicate like "shoppingList IN %@", shoppingLists. Then sort results. – Drew McCormack Jan 28 '15 at 19:15

2 Answers2

0

Your method getAllShoppingLists is returning without updating your shopping list because performQuery is an asynchronous method. It's going to perform that request in a separate thread. getAllShoppingLists will thus return immediately. The code inside the performQuery block will get called after it has retrieved those objects.

To test it out, run it with the debugger and put breakpoints inside each block. That way you can see how each one gets called.

Chris Garrett
  • 4,474
  • 30
  • 46
  • Yes, I know that! The question is how to solve this and make it work correctly. – john doe Jan 27 '15 at 03:06
  • Ah, okay. I was confused because in your code section you have the ** comment that says it returns without the number of grocery items set. Is your actual question how to do a count query for a bunch of objects? – Chris Garrett Jan 27 '15 at 16:49
0

You can solve that by making the asynchronous method synchronous. Usually this is done by using semaphores.

For a sample see: How do I wait for an asynchronously dispatched block to finish?

But it would be better to leave it async and to continue whatever you want to do at the point where you set the count. One solution for that is by passing on a code block to the getAllShoppingLists method and execute that block right after setting the count.

Community
  • 1
  • 1
Edwin Vermeer
  • 12,666
  • 2
  • 31
  • 56