1

I have a method which returns a boolean value. It should return true if at least one image/asset has been found from a URL. The code is as shown below. Inside the block when i print the count of objects in the array, it prints properly. However, outside the block, the count is zero and it does not enter the if block and the method always returns FALSE. I guess this happens because the function is returning before the block is being executed. How do I tackle this issue? How do I ensure that the method returns true if atleast one URL has been added to self.imageURLs inside the block?

-(BOOL)getPhotos
{
   self.imagesFound = FALSE;
   //get all image url's from database
   //for each row returned do the following:
        while (sqlite3_step(statement) == SQLITE_ROW)
        {
            NSString *URLString = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
            NSURL *imageURL = [NSURL URLWithString:URLString];
            ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
            [library assetForURL:imageURL resultBlock:^(ALAsset *asset)
             {
                 if (asset) {
                     //if an image exists with this URL, add it to self.imageURLs
                     [self.imageURLs addObject:URLString];
                     NSLog(@"no. of objects in array: %lu", (unsigned long)self.imageURLs.count);
                 }
             }
                    failureBlock:^(NSError *error)
             {
                 // error handling
                 NSLog(@"failure-----");
             }];       
        }                      

if (self.imageURLs.count > 0) {
    NSLog(@"self.imageURLs count = %lu", (unsigned long)self.imageURLs.count);
    self.imagesFound = TRUE;
}
else
{
        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Sorry!" message:@"No photos found" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
}
return self.imagesFound;

}
Shaik Riyaz
  • 11,204
  • 7
  • 53
  • 70
rkk817
  • 117
  • 1
  • 13

3 Answers3

0

A block can therefore maintain a set of state (data) that it can use to impact behavior when executed.

Write all code after the execution completion of your block and send a notification instead of returning the value.

[library assetForURL:imageURL resultBlock:^(ALAsset *asset)
{
  if (asset) 
   {
       //if an image exists with this URL, add it to self.imageURLs
        [self.imageURLs addObject:URLString];

        self.imagesFound = TRUE;

        //send a notificaton from here as TRUE,
   }

   }failureBlock:^(NSError *error)
   {
    //self.imagesFound = NO;
     //display error alert.
     //send a notificaton from here as FALSE,
   }];   
Shamsudheen TK
  • 30,739
  • 9
  • 69
  • 102
0

This is may be because of the blocks are sometimes asynchronous, that is after executing

[library assetForURL:imageURL resultBlock:^(ALAsset *asset).....

It wont wait for completing the execution of block, it will immediately executes the next statements.

check which log is printing first

NSLog(@"no. of objects in array: %lu", (unsigned long)self.imageURLs.count);

or a log after the

[library assetForURL:imageURL resultBlock:^(ALAsset *asset).....
Johnykutty
  • 12,091
  • 13
  • 59
  • 100
  • It prints the logs outside the block first and then the internal log. How do I make sure the block is executed synchronously? – rkk817 Jan 02 '14 at 04:51
  • From apples docs This method is asynchronous. When the asset is requested, the user may be asked to confirm the application's access to the library; the method, though, returns immediately. You should perform whatever work you want with the asset in resultBlock. See https://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/ALAssetsLibrary_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40009722-CH1-SW7 – Johnykutty Jan 02 '14 at 04:55
  • Yes that's what I'm doing... I'm adding all the URL's to the array inside the resultBlock, because I need these URL's later. Is there a way to ensure the method does not return until the result block completes execution? or is there any other work-around to implement what I'm trying? – rkk817 Jan 02 '14 at 05:00
  • There is no way to ensure the method does not return until the result block completes execution. But you can do one thing. Create a method that uses the returned result as argument. Then inside the block check if all rows processed. If all rows processed then call the method with boolean result – Johnykutty Jan 02 '14 at 05:17
0

This kind of thing is pretty much exactly what either delegates or completion handlers on blocks are for. Because your block is asynchronous, it moves on and executes the rest of your code before any chance of your boolean being set.

To do it via delegation, you would:

  1. create a class that when instantiated does what you want it to do on a background thread, and set the caller as a delegate.

  2. When it is done, call back the delegate method, and then carry your script on accordingly from there.

OR simply add a completion handler which will kick off the rest of your code that is dependent on the result that you are currently not getting at all.

In my experience, although the delegation takes longer to write, and is much more difficult to get your head around if you're new, it makes your code more reusable, and better abstracted in order to be able to be reused elsewhere in your application where the same asynchronous count or operation is required.