Can't read NSData for some photos - ios

I want to get list of thumbnails of all my camera roll photos, but for some photos I can't read NSData.
PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
for(PHAsset *asset in allPhotosResult) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(80, 80)
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
NSData *data = UIImagePNGRepresentation(result); // nil for some photos
NSString *base = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}];
}

Related

Get base64 photo from iOS photo library uri

I'm building an app in React Native, and I need to get an image from the Photo Library as base64 using the photo uri (e.g. photos://A2B9B28B-9A3E-4190-BCA0-B11F1E587085/L0/002). I was originally using the Asset Library but I've updated my app to use the new Photo Library and I'm struggling to get it to work.
My old Asset Library method was:
{
NSURL *url = [[NSURL alloc] initWithString:input];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGImageRef imageRef = [rep fullScreenImage];
NSData *imageData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
NSString *base64Encoded = [imageData base64EncodedStringWithOptions:0];
callback(#[[NSNull null], base64Encoded]);
} failureBlock:^(NSError *error) {
NSLog(#"that didn't work %#", error);
callback(#[error]);
}];
}
It is basicly the same as the code you already have, it has just been split into two parts. First get a PHAsset using an id, then get the image from that asset using the PHImageManager.
To get the PHAsset from the library use
[+ (PHFetchResult<PHAsset *> *)fetchAssetsWithALAssetURLs:(NSArray<NSURL *> *)assetURLs options:(nullable PHFetchOptions *)options;
This method is synchronous and immediately returns an array of PHAsset but these assets don't contain any image data.
next use
[PHImageManager defaultManager] to get the image. This is asynchronous like the ALAssetsLibrary method. use - (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset options:(PHImageRequestOptions *)options resultHandler:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))resultHandler;
The code should be something like this (obviously you have to fill in your error codes and error domain for your application)):
PHFetchResult* results = [PHAsset fetchAssetsWithLocalIdentifiers:#[input] options:nil];
PHAsset *asset = [results firstObject];
if (asset) {
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if (imageData){
NSString *base64Encoded = [imageData base64EncodedStringWithOptions:0];
callback(#[[NSNull null], base64Encoded]);
}else{
NSError* error = info[PHImageErrorKey];
if (!error) {
error = [NSError errorWithDomain:#"" code:-1 userInfo:#{NSLocalizedDescriptionKey:#"image not found"}];
}
callback(nil, error);
}
}];
}else{
NSError* error = [NSError errorWithDomain:#"" code:-1 userInfo:#{NSLocalizedDescriptionKey:#"asset not found"}];
callback(nil, error);
}
You can see the api in PHAsset+ (PHFetchResult<PHAsset *> *)fetchAssetsWithALAssetURLs:(NSArray<NSURL *> *)assetURLs options:(nullable PHFetchOptions *)options;

Convert AVPlayerItem to NSData

I am trying to achieve a simple task like converting a UIImage to NSData, for AVPlayerItem that is returned to me when I select a video from the PHImageManager. What might be an equivalent of the UIImagePNGRepresentation to convert video in data:
PHVideoRequestOptions *videoRequestOptions = [[PHVideoRequestOptions alloc] init];
videoRequestOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
videoRequestOptions.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:videoRequestOptions resultHandler:^(AVPlayerItem *item, NSDictionary *info)
{
//?
}];
Whereas the UIImage goes like this:
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info)
{
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(result)]; //<==THIS
}
The solution is to get the URL from the AVPlayerItem asset, then create NSData from that URL:
PHAsset *asset = ...
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:options resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
NSURL *fileURL = [(AVURLAsset *)playerItem.asset URL];
NSData *videoData = [NSData dataWithContentsOfURL:fileURL];
NSLog(#"tmpData Size: %lu",tmpData.length);
}];
and another way is to use 'requestAVAssetForVideo:asset' ....
PHFetchOptions *fetchOption = [[PHFetchOptions alloc]init];
if ([fetchOption respondsToSelector:#selector(setFetchLimit:)]) {
[fetchOption setFetchLimit:1];
}
PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:#[#"<Video-localIdentifier>"] options:fetchOption].firstObject;
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc]init];
options.version = PHVideoRequestOptionsVersionOriginal;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset
options:options
resultHandler:
^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
NSData *videoData = [NSData dataWithContentsOfURL:urlAsset.URL];
if (videoData) {
NSLog(#"videoData Size: %lu", videoData.length);
}
}];

Can't read some files from camera roll

I wrote simple library to get photos from camera roll. Unfortunately can't read some of them. I can't preview or convert to NSData
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.includeAssetSourceTypes = PHAssetSourceTypeUserLibrary;
PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];
PHImageRequestOptions *requestOptionForPhotos = [[PHImageRequestOptions alloc] init];
requestOptionForPhotos.networkAccessAllowed = YES;
for(PHAsset *asset in allPhotosResult) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(100, 100)
contentMode:PHImageContentModeAspectFill
options:requestOptionForPhotos
resultHandler:^(UIImage *result, NSDictionary *info) {
NSData *data = UIImagePNGRepresentation(result);
NSString *base = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; // for some of photos there is nil
}];
}

How to save multiple videos into application document directory?

I am using GMImagePicker to fetch assets of camera roll.
After selecting single or multiple videos i got PHAsset in arrayArray.
assetArray data in this formate
"<PHAsset: 0x7fe0e8f21130> 40D4733D-4C7B-443D-8093-C28E39ACA45E/L0/001 mediaType=2/0, sourceType=1, (720x480), creationDate=2016-02-14 09:50:58 +0000, location=0, hidden=0, favorite=0 "
How can i store this video into my application document directory?
The code i did to store video is
for (PHAsset *asset in assetArray) {
if (asset.mediaType==PHAssetMediaTypeVideo) {
NSLog(#"Video found");
}
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *URL = [(AVURLAsset *)asset URL];
NSMutableData *responseData=[[NSMutableData alloc] init];
responseData = [NSMutableData dataWithContentsOfURL:URL];
if (responseData.length>0) {
NSString * lastPath = [URL.absoluteString lastPathComponent];//videoname.mp4
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES)objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#",docDirPath,lastPath];
[responseData writeToFile:filePath atomically:YES];
}
}];
}
I also checked filepath where video is saved. Video is available but i get this error.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Please help me to solve this.
According to me use NSMutableData instead of NSData it's accept Nullable value
self.responseData = [NSMutableData dataWithContentsOfURL:location];
if (self.responseData.length>0) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString * lastPath = [url.absoluteString lastPathComponent];//videoname.mp4
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES)objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#",docDirPath,lastPath];
[self.responseData writeToFile:filePath atomically:YES];
}
});
hope this help you.
For storing both Images And Video
for (PHAsset *asset in assetArray) {
if (asset.mediaType==PHAssetMediaTypeVideo) {
NSLog(#"Video found");
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *URL = [(AVURLAsset *)asset URL];
[Albumvideos addObject:URL];
NSData *videoData = [NSData dataWithContentsOfURL:URL];
if (videoData.length>0) {
NSString *lastPath = [URL.absoluteString lastPathComponent];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#-%#",TimeStamp,lastPath]];
[fileManager createFileAtPath:fullPath contents:videoData attributes:nil];
}
}
}];
}
else{
self.requestOptions = [[PHImageRequestOptions alloc] init];
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
// this one is key
self.requestOptions.synchronous = true;
// self.assets = [NSMutableArray arrayWithArray:assets];
PHImageManager *manager = [PHImageManager defaultManager];
// assets contains PHAsset objects.
__block UIImage *ima;
// Do something with the asset
[manager requestImageForAsset:asset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:self.requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
ima = image;
[Albumimages addObject:ima];
}];
}
}
Albumvideos is mutable array which contain video URL and Albumimages contain selected images.

Read from URL obtained from PHImageFileURLKey

The Photo Framework for iOS 8 is able to return a URL for images using the PHImageFileURLKey. However I can't seem to read this URL. I only need read access. So far, any place I attempt to use the URL given, it is treated as though the file doesn't exist at all. How can I get data using the URL?
Code that successfully returns PHAssets and a URL:
PHFetchResult* phFetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil]
PHImageManager *manager = [PHImageManager defaultManager];
PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES; // Force sequential work. We have nothing to do until this block returns.
PHAsset* asset = [phFetchResult objectAtIndex:index];
[manager requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage *resultImage, NSDictionary *info)
{
_image = resultImage;
NSURL* fileURL = [info objectForKey:#"PHImageFileURLKey"];
_imageLocation = [fileURL relativePath];
}];
Code showing nothing found:
NSURL* fileURL = [info objectForKey:#"PHImageFileURLKey"];
NSLog(#"File exist?: %d", [[NSFileManager defaultManager] fileExistsAtPath:[fileURL relativePath]]);
My current workaround is to take the UIImage given and save it to the /tmp directory and then use that URL. It's not an ideal solution and I'd really like to know the purpose of the PHImageFileURLKey
I research on PHImageFileURLKey and i think there is no way to find image data by PHImageFileURLKey but you can find your image data by using asset local identifier instead of PHImageFileURLKey.
My code for your reference:
You can use your asset's local identifier and will get the image in response.
PHFetchResult *savedAssets = [PHAsset fetchAssetsWithLocalIdentifiers:#[asset.localIdentifier] options:nil];
[savedAssets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
//this gets called for every asset from its localIdentifier you saved
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
imageRequestOptions.deliveryMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager]requestImageForAsset:asset targetSize:CGSizeMake(50,50) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
NSLog(#"get image from result");
}];
}];

Resources