Reputation: 1143
I'm using Photos.Framework
to save photos taken from the camera into my gallery and to retrieve them.
This is the code I'm using to store the photos:
__block PHAssetCollection *album = [self getMyAlbumWithName:@"MyAlbumName"];
if(album == nil)
{
[self makeAlbumWithTitle:@"MyAlbumName" onSuccess:^(NSString *AlbumId) {
album = [self getMyAlbumWithName:@"MyAlbumName"];
[self addNewAssetWithImage:_imageToStore toAlbum:album onSuccess:^(NSString *ImageId)
{
_imageLocalIdentifier = imageId;
} onError:^(NSError *error) {
// No need to do anything
}];
} onError:^(NSError *error) {
// No need to do anything
}];
}
else
{
[self addNewAssetWithImage:_imageToStore toAlbum:album onSuccess:^(NSString *ImageId)
{
_imageLocalIdentifier = imageId;
} onError:^(NSError *error) {
// No need to do anything
}];
}
-(PHAssetCollection *)getMyAlbumWithName:(NSString*)AlbumName
{
PHFetchResult *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
subtype:PHAssetCollectionSubtypeAlbumRegular
options:nil];
NSLog(@"assetCollections.count = %lu", assetCollections.count);
if (assetCollections.count == 0) return nil;
__block PHAssetCollection * myAlbum;
[assetCollections enumerateObjectsUsingBlock:^(PHAssetCollection *album, NSUInteger idx, BOOL *stop) {
NSLog(@"album:%@", album);
NSLog(@"album.localizedTitle:%@", album.localizedTitle);
if ([album.localizedTitle isEqualToString:AlbumName]) {
myAlbum = album;
*stop = YES;
}
}];
if (!myAlbum) return nil;
return myAlbum;
}
-(void)makeAlbumWithTitle:(NSString *)title onSuccess:(void(^)(NSString *AlbumId))onSuccess onError: (void(^)(NSError * error)) onError
{
//Check weather the album already exist or not
if (![self getMyAlbumWithName:title])
{
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// Request editing the album.
PHAssetCollectionChangeRequest *createAlbumRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
// Get a placeholder for the new asset and add it to the album editing request.
PHObjectPlaceholder * placeHolder = [createAlbumRequest placeholderForCreatedAssetCollection];
if (placeHolder)
{
onSuccess(placeHolder.localIdentifier);
}
} completionHandler:^(BOOL success, NSError *error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
if (error)
{
onError(error);
}
}];
}
}
-(void)addNewAssetWithImage:(UIImage *)image
toAlbum:(PHAssetCollection *)album
onSuccess:(void(^)(NSString *ImageId))onSuccess
onError: (void(^)(NSError * error)) onError
{
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// Request creating an asset from the image.
PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
// Request editing the album.
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];
// Get a placeholder for the new asset and add it to the album editing request.
PHObjectPlaceholder * placeHolder = [createAssetRequest placeholderForCreatedAsset];
[albumChangeRequest addAssets:@[ placeHolder ]];
NSLog(@"%@",placeHolder.localIdentifier);
if (placeHolder) {
onSuccess(placeHolder.localIdentifier);
}
} completionHandler:^(BOOL success, NSError *error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
if (error) {
onError(error);
}
}];
}
And this is the code I'm using to retrieve this photo:
PHImageManager *imgManager = [[PHImageManager alloc] init];
PHFetchResult* fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[_imageLocalIdentifier] options:nil];
if([fetchResult count] > 0)
{
PHAsset *asset = [fetchResult objectAtIndex:0];
PHImageRequestOptions *option = [PHImageRequestOptions new];
option.synchronous = NO;
option.version = PHImageRequestOptionsVersionCurrent;
option.networkAccessAllowed = YES;
option.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
option.resizeMode = PHImageRequestOptionsResizeModeFast;
[imgManager requestImageForAsset:asset
targetSize:CGSizeMake(CAMERA_GALLERY_SIZE, CAMERA_GALLERY_SIZE)
contentMode:PHImageContentModeDefault
options:option
resultHandler:^(UIImage *result, NSDictionary *info) {
[cell.photoIV setImage:result];
}];
}
With this piece of code, over a sample of 12 photos stored (they are ok in my album) 4 or 5 of their localidentifiers returns an empty fetch results. This is tested in iOS 8, iOS 9 and iOS 10 (with iOS 10 it's indeed worse because almost all of the fetch results are empty).
I've read that something similar to this was a bug in previous versions of iOS, but I guess this is not the reason now.
I've tried with this method to retrieve the photos:
- (PHAsset *)getAssetFromGallery:(NSString *)identifier
{
PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[identifier] options:nil].lastObject;
if(asset != nil)
return asset;
__block PHAsset *result;
PHFetchResult *userAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:nil];
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
[fetchOptions setPredicate:[NSPredicate predicateWithFormat:@"localIdentifier == %@", identifier]];
[userAlbums enumerateObjectsUsingBlock:^(id _Nonnull objectCollection, NSUInteger idx, BOOL * _Nonnull stopCollectionEnumeration) {
PHAssetCollection *collection = nil;
if(![objectCollection isKindOfClass:[PHAssetCollection class]])
return;
collection = (PHAssetCollection *)objectCollection;
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions];
[assetsFetchResult enumerateObjectsUsingBlock:^(id _Nonnull objectAsset, NSUInteger idx, BOOL * _Nonnull stopAssetEnumeration) {
PHAsset *asset = nil;
if(![objectAsset isKindOfClass:[PHAsset class]])
return;
result = asset;
*stopAssetEnumeration = YES;
*stopCollectionEnumeration = YES;
}];
}];
return asset;
}
I've tried with PHAssetCollectionSubtypeAlbumMyPhotoStream
instead of PHAssetCollectionSubtypeAny
.
And I've tried with @"localIdentifier ==[cd] %@"
instead of @"localIdentifier == %@".
And always the same results, lots of times the fetch results is empty.
Any idea of what is it happening?
Upvotes: 4
Views: 2797
Reputation: 1143
My problem was that I wasn't saving the photos in the right way, I was calling onSuccess(placeHolder.localIdentifier); inside the performChanges block instead of inside the completionHandler block.
This is the code I'm using now to save the photos:
__block PHAssetCollection *album = [AuxiliaryFunctions getMyAlbumWithName:@"MyAlbumName" orWithIdentifier:@""];
if(album == nil)
[self makeAlbumWithTitle:@"MyAlbumName" onSuccess:^(NSString *AlbumId) {
album = [self getMyAlbumWithName:@"MyAlbumName" orWithIdentifier:AlbumId];
[self addNewAssetWithImage:_imageToStore toAlbum:album onSuccess:^(NSString *ImageId)
{
_imageLocalIdentifier = imageId;
} onError:^(NSError *error) {
// No need to do anything
}];
} onError:^(NSError *error) {
// No need to do anything
}];
else
{
[self addNewAssetWithImage:_imageToStore toAlbum:album onSuccess:^(NSString *ImageId)
{
_imageLocalIdentifier = imageId;
} onError:^(NSError *error) {
// No need to do anything
}];
}
-(PHAssetCollection *)getMyAlbumWithName:(NSString*)AlbumName orWithIdentifier:(NSString *)identifier
{
PHFetchResult *assetCollections = nil;
if(![identifier isEqualToString:@""])
{
PHFetchOptions *options = [PHFetchOptions new];
options.predicate = [NSPredicate predicateWithFormat:@"localIdentifier = %@ OR title = %@", identifier, AlbumName];
assetCollections = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[identifier]
options:options];
}
else
{
PHFetchOptions *options = [PHFetchOptions new];
options.predicate = [NSPredicate predicateWithFormat:@"title = %@", AlbumName];
assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
subtype:PHAssetCollectionSubtypeAny
options:options];
}
NSLog(@"assetCollections.count = %lu", assetCollections.count);
if (assetCollections.count == 0) return nil;
__block PHAssetCollection * myAlbum;
[assetCollections enumerateObjectsUsingBlock:^(PHAssetCollection *album, NSUInteger idx, BOOL *stop) {
NSLog(@"album:%@", album);
NSLog(@"album.localizedTitle:%@", album.localizedTitle);
if ([album.localizedTitle isEqualToString:AlbumName]) {
myAlbum = album;
*stop = YES;
}
}];
if (!myAlbum) return nil;
return myAlbum;
}
-(void)makeAlbumWithTitle:(NSString *)title onSuccess:(void(^)(NSString *AlbumId))onSuccess onError: (void(^)(NSError * error)) onError
{
__block NSString *localIdentifier = @"";
//Check weather the album already exist or not
if (![self getMyAlbumWithName:title orWithIdentifier:@""])
{
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// Request editing the album.
PHAssetCollectionChangeRequest *createAlbumRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
// Get a placeholder for the new asset and add it to the album editing request.
PHObjectPlaceholder * placeHolder = [createAlbumRequest placeholderForCreatedAssetCollection];
if (placeHolder)
{
localIdentifier = placeHolder.localIdentifier;
// This line was the problem
//onSuccess(localIdentifier);
}
} completionHandler:^(BOOL success, NSError *error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
if(success)
{
onSuccess(localIdentifier);
}
if (error)
{
onError(error);
}
}];
}
}
-(void)addNewAssetWithImage:(UIImage *)image
toAlbum:(PHAssetCollection *)album
onSuccess:(void(^)(NSString *ImageId))onSuccess
onError: (void(^)(NSError * error)) onError
{
__block NSString *localIdentifier = @"";
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// Request creating an asset from the image.
PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
// Request editing the album.
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];
// Get a placeholder for the new asset and add it to the album editing request.
PHObjectPlaceholder * placeHolder = [createAssetRequest placeholderForCreatedAsset];
[albumChangeRequest addAssets:@[ placeHolder ]];
NSLog(@"%@",placeHolder.localIdentifier);
if (placeHolder) {
localIdentifier = placeHolder.localIdentifier;
// This line was the problem
//onSuccess(localIdentifier);
}
} completionHandler:^(BOOL success, NSError *error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
if(success)
{
onSuccess(localIdentifier);
}
if (error)
{
onError(error);
}
}];
}
Upvotes: 3