AssetsLibrary framework broken on iOS 8

Adam Freeman picture Adam Freeman · Sep 23, 2014 · Viewed 9.4k times · Source

I have run into an issue on iOS 8 with the Assets Library framework that appears to be a bug in iOS 8. If I create an album called 'MyMedia' and then delete it, then when I try to create the album again, this chunk of code below returns 'nil' indicating that the album 'MyMedia' exists even though it does not because I deleted it using the 'Photos' app.

__block ALAssetsGroup *myGroup = nil;
__block BOOL addAssetDone = false;
NSString *albumName = @"MyMedia";
[assetsLib addAssetsGroupAlbumWithName:albumName
                           resultBlock:^(ALAssetsGroup *group) {
                               myGroup = group;
                               addAssetDone = true;
                           } failureBlock:^(NSError *error) {
                               NSLog( @"failed to create album: %@", albumName);
                               addAssetDone = true;
                           }];

while (!addAssetDone) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05f]];
}
return myGroup; // returns nil if group has previously been created and then deleted

This same method works when creating a brand new album 'MyMedia2.' Has anyone else experienced this issue and know of a workaround or solution? Is the only solution to move to the new 'Photos' framework or am I doing something incorrect here? Note that this code always works on iOS7.X

Actually the steps to reproduce this problem are as follows -> 1. Uninstall your app that takes photos and saves them to a custom album 2. Under iOS Photos delete the custom album that has saved photos in it 3. Install your app 4. If you take pictures or record videos with the app it does not create them or store them. If you look under iOS Photos albums the custom album one does not exist and none of the pictures/videos taken with the app exist.

Answer

Adam Freeman picture Adam Freeman · Oct 8, 2014

My previous answer was incorrect. I had not really tested it out. I did finally figure out what had to be done and it was difficult but I got it to work. This is what I had to do to get my app to run on both iOS 7.x.X and iOS 8.X.x and create a custom album that had been previously deleted by the app -->

  1. I wrote two chunks of code: one that uses the Photos framework on iOS 8.x.x and one that uses the AssetsLibrary framework on iOS 7.x.x

  2. Sp the app could run on both iOS versions, I linked the app to the Photos framework but then changed it from required to optional so it would not be loaded on iOS 7.x.x

  3. Because the Photos framework code could not be called directly at runtime on iOS 7.x.x, I had to change it so it loaded the classes, functions (and blocks!) dynamically at runtime

Here is the code chunk that works when running on an iPhone. This should work in the simulator too -->

// PHPhotoLibrary_class will only be non-nil on iOS 8.x.x
Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

if (PHPhotoLibrary_class) {

   /**
    *
    iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.x.x ...

    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error creating album: %@", error);
        }
    }];
    */

    // dynamic runtime code for code chunk listed above            
    id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromString(@"sharedPhotoLibrary")];

    SEL performChanges = NSSelectorFromString(@"performChanges:completionHandler:");

    NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges];

    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
    [inv setTarget:sharedPhotoLibrary];
    [inv setSelector:performChanges];

    void(^firstBlock)() = ^void() {
        Class PHAssetCollectionChangeRequest_class = NSClassFromString(@"PHAssetCollectionChangeRequest");
        SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromString(@"creationRequestForAssetCollectionWithTitle:");
        [PHAssetCollectionChangeRequest_class performSelector:creationRequestForAssetCollectionWithTitle withObject:albumName];

    };

    void (^secondBlock)(BOOL success, NSError *error) = ^void(BOOL success, NSError *error) {
       if (success) {
           [assetsLib enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
               if (group) {
                   NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
                   if ([albumName isEqualToString:name]) {
                       groupFound = true;
                       handler(group, nil);
                   }
               }
           } failureBlock:^(NSError *error) {
               handler(nil, error);
           }];
       }

       if (error) {
           NSLog(@"Error creating album: %@", error);
           handler(nil, error);
       }
   };

   // Set the success and failure blocks.
   [inv setArgument:&firstBlock atIndex:2];
   [inv setArgument:&secondBlock atIndex:3];

   [inv invoke];

}
else {   
   // code that always creates an album on iOS 7.x.x but fails
   // in certain situations such as if album has been deleted
   // previously on iOS 8...x. .              
   [assetsLib addAssetsGroupAlbumWithName:albumName
       resultBlock:^(ALAssetsGroup *group) {
       handler(group, nil);
   } failureBlock:^(NSError *error) {
       NSLog( @"Failed to create album: %@", albumName);
       handler(nil, error);
   }];
}