Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use PHAssetResourceRequestOptions progressHandler/PHAssetResourceProgressHandler

I'm trying to retrieve/download the video/frames of Live Photo. As for the API documents there is a possible scenario which the Live Photos will be store at iCloud. In order to retrieve them as well you need to declare

let options = PHAssetResourceRequestOptions()
        options.networkAccessAllowed = true  

I'm trying to create a progress bar while the Live Photo is being download. According to the API, you need to declare this properties:

public var progressHandler: PHAssetResourceProgressHandler?

progress    
A floating-point value indicating the progress of the download. 
A value of 0.0 indicates that the download has just started,
and a value of 1.0 indicates the download is complete. 

I haven't found the correct way to retrieve those yet. Any suggestion?

Full Code :

 let assestResource = PHAssetResource.assetResourcesForAsset(asset)
 let options = PHAssetResourceRequestOptions()
 options.networkAccessAllowed = true
for assetRes in assestResource {
            print(assetRes.type.rawValue)
            if (assetRes.type == .PairedVideo) {
                print("imageTaken")
                manager.writeDataForAssetResource(assetRes, toFile: documentsURL,    options: options, completionHandler: { (error) -> Void in
                    if error == nil
                    {

                    }
                    else
                    {
                        print(error)
                    }
                })
like image 736
Roi Mulia Avatar asked Feb 06 '16 13:02

Roi Mulia


2 Answers

Yes, unfortunately, there is an Apple bug with iCloud downloads + PHAssetResourceManager. I get the following error regardless of the asset type:

Error: Missing resource download context

Instead, use PHImageManager. You need to have a unique request for each type of PHAsset:

- (void)downloadAsset:(PHAsset *)asset toURL:(NSURL *)url completion:(void (^)(void))completion
{
    if (asset.mediaType == PHAssetMediaTypeImage && (asset.mediaSubtypes & PHAssetMediaSubtypePhotoLive))
    {
        PHLivePhotoRequestOptions *options = [PHLivePhotoRequestOptions new];
        options.networkAccessAllowed = YES;
        [[PHImageManager defaultManager] requestLivePhotoForAsset:asset targetSize:CGSizeZero contentMode:PHImageContentModeAspectFill options:options resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
            if ([info objectForKey:PHImageErrorKey] == nil)
            {
                NSData *livePhotoData = [NSKeyedArchiver archivedDataWithRootObject:livePhoto];
                if ([[NSFileManager defaultManager] createFileAtPath:url.path contents:livePhotoData attributes:nil])
                {
                    NSLog(@"downloaded live photo:%@", url.path);
                    completion();
                }
            }
        }];
    }
    else if (asset.mediaType == PHAssetMediaTypeImage)
    {
        PHImageRequestOptions *options = [PHImageRequestOptions new];
        options.networkAccessAllowed = YES;
        [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
            if ([info objectForKey:PHImageErrorKey] == nil
                && [[NSFileManager defaultManager] createFileAtPath:url.path contents:imageData attributes:nil])
            {
                NSLog(@"downloaded photo:%@", url.path);
                completion();
            }
        }];
    }
    else if (asset.mediaType == PHAssetMediaTypeVideo)
    {
        PHVideoRequestOptions *options = [PHVideoRequestOptions new];
        options.networkAccessAllowed = YES;
        [[PHImageManager defaultManager] requestExportSessionForVideo:asset options:options exportPreset:AVAssetExportPresetHighestQuality resultHandler:^(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info) {
            if ([info objectForKey:PHImageErrorKey] == nil)
            {
                exportSession.outputURL = url;

                NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:asset];
                for (PHAssetResource *resource in resources)
                {
                    exportSession.outputFileType = resource.uniformTypeIdentifier;
                    if (exportSession.outputFileType != nil)
                        break;
                }

                [exportSession exportAsynchronouslyWithCompletionHandler:^{
                    if (exportSession.status == AVAssetExportSessionStatusCompleted)
                    {
                        NSLog(@"downloaded video:%@", url.path);
                        completion();
                    }
                }];
            }
        }];
    }
}
like image 120
owjhart Avatar answered Nov 12 '22 16:11

owjhart


@owjhart: Great solution!!!

For those, who would like to grab life photos as movie files, I've adopted the code a bit :-)

- (void)downloadAsset:(PHAsset *)pAsset
           completion:(void (^)(BOOL pSucceeded, NSURL* pURL))pCompletion {
    NSParameterAssert(pCompletion);


    if ((PHAssetMediaTypeImage == pAsset.mediaType) &&
        (pAsset.mediaSubtypes & PHAssetMediaSubtypePhotoLive)) {

        NSArray*            assetResources = [PHAssetResource assetResourcesForAsset:pAsset];
        PHAssetResource*    assetResource = nil;
        for (PHAssetResource* asress in assetResources) {
            if (UTTypeConformsTo((__bridge CFStringRef)asress.uniformTypeIdentifier, kUTTypeMovie)) {
                assetResource = asress;
                break;
            }
        }

        if (assetResource) {
            __block NSMutableData*      assetData = NSMutableData.new;
            [PHAssetResourceManager.defaultManager requestDataForAssetResource:assetResource
                                                                       options:nil
                                                           dataReceivedHandler:^(NSData * _Nonnull pData) {
                                                               [assetData appendData:pData];
                                                           }
                                                             completionHandler:^(NSError * _Nullable pError) {
                                                                 //kUTTypeLivePhoto
                                                                 CFStringRef extension = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)assetResource.uniformTypeIdentifier, kUTTagClassFilenameExtension);
                                                                 NSString*  filename = [NSString stringWithFormat:@"%@.%@", NSProcessInfo.processInfo.globallyUniqueString, extension];
                                                                 NSURL*     tempURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()
                                                                                               isDirectory:YES] URLByAppendingPathComponent:filename];
                                                                 if ((!pError) &&
                                                                     (assetData.length) &&
                                                                     ([NSFileManager.defaultManager createFileAtPath:tempURL.path
                                                                                                            contents:assetData
                                                                                                          attributes:nil])) {
                                                                     pCompletion(YES, tempURL);
                                                                 }
                                                                 else {
                                                                     pCompletion(NO, nil);
                                                                 }
                                                             }];
        }
        else {
            pCompletion(NO, nil);
        }
    }
    else if (PHAssetMediaTypeImage == pAsset.mediaType) {

        PHImageRequestOptions*  options = PHImageRequestOptions.new;
        options.networkAccessAllowed = YES;
        [PHImageManager.defaultManager requestImageDataForAsset:pAsset
                                                        options:options
                                                  resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {

            CFStringRef extension = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)dataUTI, kUTTagClassFilenameExtension);
            NSString*   filename = [NSString stringWithFormat:@"%@.%@", NSProcessInfo.processInfo.globallyUniqueString, (__bridge NSString*)extension];
            NSURL*      tempURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()
                                          isDirectory:YES] URLByAppendingPathComponent:filename];
            if ((nil == [info objectForKey:PHImageErrorKey]) &&
                ([NSFileManager.defaultManager createFileAtPath:tempURL.path 
                                                       contents:imageData
                                                     attributes:nil])) {
                NSLog(@"downloaded photo:%@", tempURL.path);
                pCompletion(YES, tempURL);
            }
            else {
                pCompletion(NO, nil);
            }
        }];
    }
    else if (PHAssetMediaTypeVideo == pAsset.mediaType) {

        PHVideoRequestOptions*  options = PHVideoRequestOptions.new;
        options.networkAccessAllowed = YES;
        [PHImageManager.defaultManager requestExportSessionForVideo:pAsset
                                                            options:options
                                                       exportPreset:AVAssetExportPresetHighestQuality
                                                      resultHandler:^(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info) {
            if (nil == [info objectForKey:PHImageErrorKey]) {
                NSArray<PHAssetResource*>*  resources = [PHAssetResource assetResourcesForAsset:pAsset];
                for (PHAssetResource* resource in resources) {
                    exportSession.outputFileType = resource.uniformTypeIdentifier;
                    if (nil != exportSession.outputFileType) {
                        break;
                    }
                }
                CFStringRef extension = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)exportSession.outputFileType, kUTTagClassFilenameExtension);
                NSString*   filename = [NSString stringWithFormat:@"%@.%@", NSProcessInfo.processInfo.globallyUniqueString, (__bridge NSString*)extension];
                NSURL*      tempURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()
                                              isDirectory:YES] URLByAppendingPathComponent:filename];
                exportSession.outputURL = tempURL;


                [exportSession exportAsynchronouslyWithCompletionHandler:^{
                    if (AVAssetExportSessionStatusCompleted == exportSession.status) {
                        NSLog(@"downloaded video:%@", tempURL.path);
                        pCompletion(YES, tempURL);
                    }
                    else {
                        pCompletion(NO, nil);
                    }
                }];
            }
            else {
                pCompletion(NO, nil);
            }
        }];
    }
    else {
        pCompletion(NO, nil);
    }
}
like image 1
LaborEtArs Avatar answered Nov 12 '22 16:11

LaborEtArs