1

I'm trying to pick out some camera roll meta data. When I enumerate through the assets, I cannot seem to retrieve any information and get an empty array. Is there a step I'm missing?

My code:

assets = [[NSMutableArray array] init]; 

void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
    if(asset != NULL) {
        [assets addObject:asset];
        dispatch_async(dispatch_get_main_queue(), ^{
        });
    }
};

void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) =  ^(ALAssetsGroup *group, BOOL *stop) {
    if(group != nil) {
        [group enumerateAssetsUsingBlock:assetEnumerator];
    }
};

library = [[ALAssetsLibrary alloc] init];

[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos 
                       usingBlock:assetGroupEnumerator
                     failureBlock: ^(NSError *error) {
                         NSLog(@"Failed.");
                     }];    

NSLog(@"%@", assets); //prints an empty array
HHHH
  • 1,159
  • 2
  • 16
  • 28
  • Thanks for the answers. Just a further q - is there an easy way to pick out photos that were taken on a certain date without having to enumerate through all assets? – HHHH Nov 10 '12 at 07:35

3 Answers3

4

Midhun MP is right that you are not waiting for the asynchronous enumeration to complete. In this case, you have asynchronous blocks calling other asynchronous blocks, so it is not simple to know when all enumeration is done.

If you would like to know when this is done, and end up with an array that contains all of the enumerated assets, you could use dispatch_groups. Here is one way you could do that (I've included multiple ALAssetGroup types to show that this can work with multiple albums):

dispatch_group_t loadingGroup = dispatch_group_create();
NSMutableArray * assets = [[NSMutableArray array] init];
NSMutableArray * albums = [[NSMutableArray array] init];

void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
    if(index != NSNotFound) {
        [assets addObject:asset];
        dispatch_async(dispatch_get_main_queue(), ^{ });
    } else {
        dispatch_group_leave(loadingGroup);
    }
};


void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) =  ^(ALAssetsGroup *group, BOOL *stop) {
    if(group != nil) {
        [albums addObject: group];
    } else {
         NSLog(@"Found %d albums", [albums count]);
         // album loading is done
        for (ALAssetsGroup * album in albums) {
            dispatch_group_enter(loadingGroup);
            [album enumerateAssetsUsingBlock: assetEnumerator];
        }
        dispatch_group_notify(loadingGroup, dispatch_get_main_queue(), ^{
            NSLog(@"DONE: ALAsset array contains %d elements", [assets count]);
        });
    }
};

ALAssetsLibrary * library = [[ALAssetsLibrary alloc] init];

[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos | ALAssetsGroupAlbum
                   usingBlock:assetGroupEnumerator
                 failureBlock: ^(NSError *error) {
                     NSLog(@"Failed.");
                 }];

(In this example, it is safe to have various blocks adding to assets and albums because the enumeration is all happening on the main thread.)

Peter E
  • 4,771
  • 1
  • 17
  • 14
  • Was hard picking a correct answer as all pointed towards my problem, but this one was most comprehensive. Thanks! – HHHH Nov 10 '12 at 07:30
  • When using **assetEnumerator** to enumerate asset, should we synchronize accessing to **assets** array. Like `@synchronized(self) { [assets addObject:asset]; }` – OnTheEasiestWay Mar 25 '14 at 12:07
  • 3 years later and this post has saved me, thank you! – MarioAna Aug 09 '15 at 18:39
0

If you are running this on iOS 6, it may be that the user has denied access for you app to access the asset library. If this is the case, the failureBlock is called.

Also note that the usingBlock is called asynchronously so your attempt to log assets is premature. You should move the NSLog statement into the end of the enumeration block.

From the docs for enumerateGroupsWithTypes:usingBlock:failureBlock:

The results are passed one by one to the caller by executing the enumeration block.

This method is asynchronous. When groups are enumerated, the user may be asked to confirm the application's access to the data; the method, though, returns immediately. You should perform whatever work you want with the assets in enumerationBlock.

If the user denies access to the application, or if no application is allowed to access the data, the failureBlock is called.

Community
  • 1
  • 1
rmaddy
  • 298,130
  • 40
  • 468
  • 517
0

Your NSLog will always display an empty array because the NSLog statement will work before completing the asynchronous enumeration block.

Solution:

First check that your photolibrary is not empty. Then add NSLog in:

void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
    if(asset != NULL) {
        [assets addObject:asset];
        NSLog(@"Asset : %@", asset);
        dispatch_async(dispatch_get_main_queue(), ^{
        });
    }
};
Midhun MP
  • 90,682
  • 30
  • 147
  • 191