Photos Framework requestImageDataForAsset occasionally fails
Asked Answered
F

4

21

I'm using the photos framework on iOS8.1 and requesting the image data for the asset using requestImageDataForAsset... Most of the time it works and I get the image data and a dictionary containing what you see below. But sometimes the call completes, but the data is nil and the dictionary contains three generic looking entries.

The calls are performed sequentially and on the same thread. It is not specific to any particular image. The error will happen on images I've successfully opened in the past. Has anyone encountered this?

+ (NSData *)retrieveAssetDataPhotosFramework:(NSURL *)urlMedia resolution:(CGFloat)resolution imageOrientation:(ALAssetOrientation*)imageOrientation {

    __block NSData *iData = nil;

    PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[urlMedia] options:nil];
    PHAsset *asset = [result firstObject];

    PHImageManager *imageManager = [PHImageManager defaultManager];
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
    options.synchronous = YES;
    options.version = PHImageRequestOptionsVersionCurrent;

    @autoreleasepool {
        [imageManager requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
            iData = [imageData copy];
            NSLog(@"requestImageDataForAsset returned info(%@)", info);
            *imageOrientation = (ALAssetOrientation)orientation;
        }];
    }

    assert(iData.length != 0);
    return iData;
}

This is the desired result where I get image data and the dictionary of meta data:

requestImageDataForAsset returned info({
    PHImageFileDataKey = <PLXPCShMemData: 0x1702214a0> bufferLength=1753088 dataLength=1749524;
    PHImageFileOrientationKey = 1;
    PHImageFileSandboxExtensionTokenKey = "6e14948c4d0019fbb4d14cc5e021199f724f0323;00000000;00000000;000000000000001a;com.apple.app-sandbox.read;00000001;01000003;000000000009da80;/private/var/mobile/Media/DCIM/107APPLE/IMG_7258.JPG";
    PHImageFileURLKey = "file:///var/mobile/Media/DCIM/107APPLE/IMG_7258.JPG";
    PHImageFileUTIKey = "public.jpeg";
    PHImageResultDeliveredImageFormatKey = 9999;
    PHImageResultIsDegradedKey = 0;
    PHImageResultIsInCloudKey = 0;
    PHImageResultIsPlaceholderKey = 0;
    PHImageResultWantedImageFormatKey = 9999;
})

Here's what I get occasionally. image data is nil. Dictionary contains not so much.

requestImageDataForAsset returned info({
    PHImageResultDeliveredImageFormatKey = 9999;
    PHImageResultIsDegradedKey = 0;
    PHImageResultWantedImageFormatKey = 9999;
})
Friulian answered 27/1, 2015 at 12:22 Comment(3)
It also happens to me, for images in shared iCloud streams. No solution yet.Fester
It appears to happen regularly (near 100%) when trying to use options.synchronous = YES to fetch the image for an asset in an iCloud photo stream (a.k.a., Shared Album) that has not been downloaded yet. If the image has been downloaded and cached either by the Photos app or asynchronously by my app, it works fine. Otherwise, the synchronous download always fails. I've tried iOS 8.x, 9.0 and 9.1 with the same results.Peking
I also encounter this issue. My workaround is to set options.synchronous = NOYonder
H
2

You are likely iterating through an array, and memory is not freed timely, you can try the below code. Make sure theData is marked by __block.

@autoreleasepool {
    [imageManager requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
        NSLog(@"requestImageDataForAsset returned info(%@)", info);
        theData = [imageData copy];
    }];
}
Hydrotherapy answered 27/1, 2015 at 12:53 Comment(5)
Thanks gabbler. @The autoreleasepool does help with reducing the memory usage under load (retrieving several images in a row), but the issue persists.Friulian
Post your code of retrieving several images in a row.Hydrotherapy
I've updated the post to show the entire method. I've wrapped the requestImageDataForAsset in a class method to shield the caller from the caring about the version of iOS. There is a similar method for the ALAssetsLibrary that gets called on iOS7. The code is not called in a loop, rather multiple events can occur where different images are requested about the same time. The problem is inconsistent. It has not happened at all in the past day. Sometimes it happens here and there. Sometimes it happens 50% of the time a photo is requested.Friulian
Worked for me. I was indeed looping and around the 800th image it wouldn't return any data. This should be marked correct.Kranz
This is NOT a fix: I can recreate this consistently with the same 4 assets in my personal (iCloud photo) library. The issue appears to be a genuine bug in the Photos framework, or some issue with those particular assets in the library. The question now, is what is an effective workaround.Onyx
O
5

I had a problem with similar symptoms where requestImageDataForAsset returned nil image data but was also accompanied by a console error message like this:

[Generic] Failed to load image data for asset <PHAsset: 0x13d041940> 87CCAFDC-A0E3-4AC9-AD1C-3F57B897A52E/L0/001 mediaType=1/0, sourceType=2, (113x124), creationDate=2015-06-29 04:56:34 +0000, location=0, hidden=0, favorite=0 with format 9999

In my case, the problem suddenly started happening on a specific device only with assets in iCloud shared albums after upgrading from iOS 10.x to 11.0.3, and since then through to 11.2.5. Thinking that maybe requestImageDataForAsset was trying to use files locally cached in /var/mobile/Media/PhotoData/PhotoCloudSharingData/ (from the info dictionary's PHImageFileURLKey key) and that the cache may be corrupt I thought about how to clear that cache.

Toggling the 'iCloud Photo Sharing' switch in iOS' Settings -> Accounts & Passwords -> iCloud -> Photos seems to have done the trick. requestImageDataForAsset is now working for those previously failing assets.

Update 9th March 2018

I can reproduce this problem now. It seems to occur after restoring a backup from iTunes:

  1. Use the iOS app and retrieve photos from an iCloud shared album.
  2. Backup the iOS device using iTunes.
  3. Restore the backup using iTunes.
  4. Using the app again to retrieve the same photos from the iCloud shared album now fails with the above console message.

Toggling the 'iCloud Photo Sharing' switch fixes it still. Presumably the restore process somehow corrupts some cache. I've reported it as Bug 38290463 to Apple.

Otherworld answered 9/2, 2018 at 1:29 Comment(7)
But how do you tell the user to do so? Would be awesome to have a solution for the app itself.Monday
@Monday That's a good point. Having the app accurately detect this situation itself and then perhaps directing the user to a support page describing what to try to rectify the problem would be ideal. Accurately detecting the problem could be tricky though as all you really have from within the app is a nil result, with no other information to discriminate between other possible failure causes.Otherworld
"I've reported it as Bug 38290463 to Apple" could you please provide the link for it so we can review it?Sawyor
@AhmadF Here's the link: https://bugreport.apple.com/web/?problemID=38290463 but I don't think Apple's system allows external people to view each other's bug reports so I'm not sure that it'll be of much use.Otherworld
"We're sorry, something went wrong. This bug does not exist or you do not have access." Thanks anyway ;)Sawyor
this is not a bug. It is related to Optimize iPhone Storage option in Settings -> Passwords & Accounts -> iCloud -> Photos. Change to Download and Keep Originals or pass PHImageRequestOptions.isNetworkAccessAllowed = true to requestImageData(for asset: options: resultHandler:)Megaera
I got the error because I did not have a progress handler so i needed options.synchronous = YES;Vivia
H
2

You are likely iterating through an array, and memory is not freed timely, you can try the below code. Make sure theData is marked by __block.

@autoreleasepool {
    [imageManager requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
        NSLog(@"requestImageDataForAsset returned info(%@)", info);
        theData = [imageData copy];
    }];
}
Hydrotherapy answered 27/1, 2015 at 12:53 Comment(5)
Thanks gabbler. @The autoreleasepool does help with reducing the memory usage under load (retrieving several images in a row), but the issue persists.Friulian
Post your code of retrieving several images in a row.Hydrotherapy
I've updated the post to show the entire method. I've wrapped the requestImageDataForAsset in a class method to shield the caller from the caring about the version of iOS. There is a similar method for the ALAssetsLibrary that gets called on iOS7. The code is not called in a loop, rather multiple events can occur where different images are requested about the same time. The problem is inconsistent. It has not happened at all in the past day. Sometimes it happens here and there. Sometimes it happens 50% of the time a photo is requested.Friulian
Worked for me. I was indeed looping and around the 800th image it wouldn't return any data. This should be marked correct.Kranz
This is NOT a fix: I can recreate this consistently with the same 4 assets in my personal (iCloud photo) library. The issue appears to be a genuine bug in the Photos framework, or some issue with those particular assets in the library. The question now, is what is an effective workaround.Onyx
F
0

Getting back to this after a long while, I have solved a big part of my problem. No mystery, just bad code:

PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[urlMedia] options:nil];
PHAsset *asset = [result firstObject];

if (asset != nil) { // the fix
    PHImageManager *imageManager = [PHImageManager defaultManager];
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
    ...
}

The most common cause for me was a problem with the media URL passed to fetchAssetsWithALAssetURLs causing asset to be nil and requestImageDataForAsset return a default info object.

Friulian answered 7/12, 2016 at 2:32 Comment(1)
Did you solve your problem entirely of 'not' getting nil assets?Quartziferous
A
0

The following code maybe help. I think the class PHImageRequestOptions has a bug, so I pass nil , and then fix the bug.

   dispatch_semaphore_t sema = dispatch_semaphore_create(0);
            [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
                assetModel.size = imageData.length;
                NSString *filename = [asset valueForKey:@"filename"];
                assetModel.fileName = filename;
                dispatch_semaphore_signal(sema);
            }];
            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
Andrel answered 4/11, 2019 at 1:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.