PHAsset Null but local identifier is valid - ios

I am trying to load an asset from a local identifier. The local identifier seems to be correct but the asset is null and I can't figure out why. I have a similar code in another part of my app that works fine.
Queue *queue = [NSEntityDescription insertNewObjectForEntityForName:#"Queue" inManagedObjectContext:self.context];
for (int reyrt = 0; reyrt < self.storeGIF.count; reyrt++) {
queue.queuetextimagePath = [self.storeGIF objectAtIndex:reyrt];
}
__block float fileSize;
NSArray *identifiers = #[queue.queuetextimagePath];
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:identifiers options:nil];
PHAsset *asset = [assetsFetchResult firstObject];
NSLog (#"identifiers: %#", identifiers);
NSLog (#"assetsFetchResult: %#", assetsFetchResult);
NSLog (#"asset: %#", asset);
if (!asset) {
NSLog(#"can't retrieve PHAsset from localIdentifier:%#",identifiers);
}
Here is the NSLog file result when using the above code.
019-07-19 09:26:36.366739-0400 myApp[1440:328144] identifiers: (
"857AC3DA-C047-4D88-911B-C5FE227E2B96/L0/001"
)
2019-07-19 09:26:36.366945-0400 myApp[1440:328144] assetsFetchResult PHFetchResult: 0x281129900 count=0
2019-07-19 09:26:36.366982-0400 myApp[1440:328144] asset: (null)
2019-07-19 09:26:36.367061-0400 myApp[1440:328144] can't retrieve PHAsset from localIdentifier:(
"857AC3DA-C047-4D88-911B-C5FE227E2B96/L0/001"
)

The problem is that you should drop /L0/001 part before requesting the object. I don't understand why uuid strings contain these suffixes, but it works if you drop it and use only normal UUID part.

Related

PHAsset assetResourcesForAsset fails when called too often

I need to retrieve the names of all the PHAsset existing in the Camera Roll, individually and in a short time.
To get the file name, I use the documented originalFilename property of PHAssetResource associated to the PHAsset.
This works fine for the first assets, but at some point (after around 400 assets), it starts failing and returning nil every time.
Here is a code that shows this behavior (running on an iPhone 7 with ~800 photos in the Camera Roll):
PHFetchResult *result = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil];
PHAssetCollection *assetCollection = result.firstObject;
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
for (int i = index; i<[assets count]; i++) {
PHAsset *asset = assets[i];
NSArray *resources = [PHAssetResource assetResourcesForAsset:asset];
NSString *name = (resources.count > 0) ? [(PHAssetResource*)resources.firstObject originalFilename] : nil;
NSLog(#"%i: %#", i, name);
}
When using undocumented methods to get the file name, such as [asset valueForKey#"filenamme"] or the PHImageFileURLKey key of the info dictionary returned by the PHImageManager, everything works well (although the name is different than with the originalFilename and well, it's not reliable since it's not documented).
How come the official method is that unreliable?
Is there something I do wrong?

PhotoKit: fetchAssetCollectionsWithLocalIdentifiers fails

The following code highlights the issue in 3 simple steps:
1) fetch moments
2) cache moment localIdentifier
3) fetch moment with identifier : fail( on device, iOS 8.2 )
- ( void )momentLocalIdTest
{
PHFetchResult * fetchResult;
PHAssetCollection * moment;
NSString * localIdentifier;
fetchResult = [ PHAssetCollection fetchMomentsWithOptions: nil ];
if( fetchResult.count == 0 )
return;
moment = fetchResult.firstObject;
localIdentifier = moment.localIdentifier;
fetchResult = [ PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers: #[ localIdentifier ] options: nil ];
if( fetchResult.count == 0 )
NSLog( #"AssetCollection with localIdentifier %# not found!!!", localIdentifier );
}
Am I misunderstanding something? It seems pretty straightforward...
Any help appreciated!
I encountered the same issue as well, and could not figure out what is wrong with this code. I think this API is just plain and simply broken (8.0 through 8.4 at least)
Here's a workaround code; you basically have to respawn the PHAssetCollection instance from the identifier
PHFetchOptions *options = [PHFetchOptions new];
options.predicate = [NSPredicate predicateWithFormat:#"localIdentifier = %#", identifier];
PHAssetCollection *collection = [[PHAssetCollection fetchMomentsWithOptions:options] firstObject];
PHFetchResult *results = [PHAsset fetchAssetsInAssetCollection:collection options:nil];

Check if PHAsset exists in PHFetchResult

how to know if asset for local identifier is not found. I have the list of localIDs of each photos and videos been fetched from the photos framework, how to know if the photo is present or not in the iOS photo album.
You need to keep track on the number of assets in that userAlbums and if you didn't find the asset until the last asset is checked return the Not Found notification.
You can do it like:
NSString *localId = /*local identifier of photo */;
PHFetchResult *userAlbums = [PHAsset fetchAssetsWithLocalIdentifiers:#[localId] options:nil];
NSUInteger assetCount = [userAlbums count];
__block NSUInteger assetCounter = 0;
[userAlbums enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop)
{
// Check whether you found the asset or not
if (/*asset found*/)
{
// Asset found, set the stop bool to yes
}
else if (assetCounter == assetCount)
{
//Data not found yet
}
}];
Why not just use:
if ([userAlbums count]) {
//At least one item found.
}
else {
//Nothing found
}

ISRC Code from AVMetadataItem objective c

I want to get the ISRC code for Local itunes songs. I can get the metaData by the following codes:
MPMusicPlayerController *mp= mp = [MPMusicPlayerController applicationMusicPlayer];
NSURL *assetURL = [mp.nowPlayingItem valueForProperty:MPMediaItemPropertyAssetURL];
AVAsset *asset = [AVAsset assetWithURL:assetURL];
NSArray *metadata = [asset commonMetadata];
for ( AVMetadataItem* item in metadata ) {
NSString *key = [item commonKey];
NSString *value = [item stringValue];
NSLog(#"extra iptions %#",[item extraAttributes]);
NSLog(#"key = %#, value = %#", key, value);
NSLog(#"keyspace and Local %# %#",[item keySpace],[item key]);
}
But I am really wondering about how to get ISRC(International Standard Record Coding).
Try this (warning: typed into browser)
NSArray *metadata = [asset metadataForFormat:AVMetadataFormatID3Metadata];
if (metadata == nil) {
NSLog(#"No ID3 metadata for asset: %#", asset);
}
// From https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVFoundation_ID3Constants/Reference/reference.html
NSArray *filteredMetadata = [AVMetadataItem metadataItemsFromArray:metadata withKey:AVMetadataID3MetadataKeyInternationalStandardRecordingCode keySpace:nil];
AVMetadataItem *item = [filteredMetadata firstObject];
if (item != nil) {
NSLog(#"ISRC: %#", item.stringValue);
} else {
NSLog(#"No ISRC found for: %#", asset);
}
EDIT: I should mention, the reason your original code didn't print the value of the ISRC is because the ISRC is not part of the common metadata space, and won't be included in the array returned by [asset commonMetadata]. The ISRC key is specific to ID3 metadata, so if your asset does not have ID3 metadata associated with it, you will be unable to retrieve that information.

Looking for a Logic Solution to Build Dynamic Filepath in iOS

I have a file structure which is being built in the following manner.
1) Each user is provided an ID in MySQL database.
2) When user uploads an image from iphone the image is placed in the following directory:
`../images/[USER_ID]/[IMAGE_NUMBER].jpg`
3) In the path noted above, [IMAGE_NUMBER] is generated based upon the submission value. So the first image uploaded will be titled 1.jpg, the second as 2.jpg etc…
So to build a sample directory for the purposes of this question the structure could look something like this:
../images-->
../10/1.jpg
../10/2.jpg
../10/3.jpg
../11/4.jpg
../11/5.jpg
../10/6.jpg
../11/7.jpg
So in this case, User #10 uploaded 3 images, then logged out. Along comes User #11 and she uploads 2 images before logging out. And finally, User #10 logs back in and uploads another image.
Okay now that we have the summary of out the directories are being generated and images are dynamically input, let me get to the ultimate question. I would like to be able to display thumbnails of all the images on a view in the iphone when a user clicks a button that we will call btnRefresh
Here is the method that is associated with btnRefresh:
-(IBAction)btnRefreshTapped
{
[self refreshStream];
}
-(void)refreshStream {
[[API sharedInstance] commandWithParams:[NSMutableDictionary dictionaryWithObjectsAndKeys:
#"stream",#"command",
nil]
onCompletion:^(NSDictionary *json) {
[self showStream:[json objectForKey:#"result"]];
}];
}
As you can see this is referencing the API sharedInstance here is that method:
+(API*)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kAPIHost]];
});
return sharedInstance;
}
and finally we come full circle to the URLWithString value:
-(NSURL*)urlForImageWithId:(NSNumber*)IdPhoto isThumb:(BOOL)isThumb {
int IdValue = [[user objectForKey:#"id"] intValue];
NSString* urlString = [NSString stringWithFormat:#"%#/%#upload/%d/%#%#.jpg",
kAPIHost, kAPIPath, IdValue, IdPhoto, (isThumb)?#"-thumb":#""
];
return [NSURL URLWithString:urlString];
}
So, herein lies my problem. As this is currently being defined I can only see the images of the user that is logged into the app when they click btnRefresh. The other image locations show up as greyed out images due to the broken URLs. So, how can I redefine IdValue to cycle through available folders and pull the associated images for display?
I know this is a complex problem so thank you to putting some brainpower into a solution.
I don't know is it what you asking but you can get all files from the directory via this code:
NSArray *contents = [fileManager contentsOfDirectoryAtURL:YourURL
includingPropertiesForKeys:#[] // <-Add key/s if needed
options:NSDirectoryEnumerationSkipsHiddenFiles error:nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"pathExtension == 'jpg'"];
for (NSURL *fileURL in [contents filteredArrayUsingPredicate:predicate])
{
// Enumerate each .jpg file in directory
}
You can specify images directory and you should be able to find the images and get url to that.
The other way is recursively enumerate files in directory, it's more powerful and it can be amended to your requirement:
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:YOURURL
includingPropertiesForKeys:#[NSURLNameKey, NSURLIsDirectoryKey] // <-Add more key if needed
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:^BOOL(NSURL *url, NSError *error)
{
NSLog(#"Error %#", error);
}];
NSMutableArray *mutableFileURLs = [NSMutableArray array];
for (NSURL *fileURL in enumerator) {
NSString *filename;
[fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil];
NSNumber *isDirectory;
[fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
if (![isDirectory boolValue]) {
[mutableFileURLs addObject:fileURL];
}
}
Hope one of those will be usefull for you.
Modifying what Greg just answered so that you don't have to iterate the array.
NSArray *paths = [fileManager contentsOfDirectoryAtURL:YourURL
includingPropertiesForKeys:#[]
options:NSDirectoryEnumerationSkipsHiddenFiles error:nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
[NSString stringWithFormat:
#"self ENDSWITH '/filename.jpg'"]];
NSArray *filteredArray = [paths filteredArrayUsingPredicate:predicate];
if ([newarray lastObject]) {
//you have your path here
}
filename.jpg is the IdPhoto.jpg in your code. This code with work because of the type of data you have. I have included "/" in filename.jpg so that there are no issues in detecting 110.jpg vs 10.jpg.

Resources