Wait for assetForURL blocks to be completed - iphone

I would like to wait this code to be executed before to continue but as these blocks are called assynchronously I don't know how to do???
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
}];
}

GCD semaphore approach:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for (NSURL *url in self.assetUrls) {
dispatch_async(queue, ^{
[library assetForURL:url resultBlock:^(ALAsset *asset) {
[self.assets addObject:asset];
dispatch_semaphore_signal(sema);
} failureBlock:^(NSError *error) {
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
dispatch_release(sema);
/* Check out ALAssets */
NSLog(#"%#", self.assets);

The solution is here
http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/

Note that assetForURL:resultBlock:failureBlock: will stuck if the main thread is waiting without RunLoop running. This is alternative ( cleaner :-) ) solution:
#import <libkern/OSAtomic.h>
...
ALAssetsLibrary *library;
NSMutableArray *assets;
...
__block int32_t counter = 0;
for (NSURL *url in urls) {
OSAtomicIncrement32(&counter);
[library assetForURL:url resultBlock:^(ALAsset *asset) {
if (asset)
[assets addObject:asset];
OSAtomicDecrement32(&counter);
} failureBlock:^(NSError *error) {
OSAtomicDecrement32(&counter);
}];
}
while (counter > 0) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}

The easiest thing to do is to move your code to inside (at the end of) the resultBlock or the failureBlock. That way, your code will run in the correct order, and you will also retain asynchronous behaviour.

This is an easy way to do it. Maybe not as elegant as using GCD but it should get the job done ... This will make your method blocking instead of non-blocking.
__block BOOL isFinished = NO;
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
if (objectsToRemove.count + tmpListAsset.count == assetsList.count) {
isFinished = YES;
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
isFinished = YES;
}];
}
while (!isFinished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];
}

Related

Saving Video in an Album Created

i have created an album using this code in AppDelegate methode
NSString *albumName=#"999Videos";
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:albumName
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"added album:%#", albumName);
}
failureBlock:^(NSError *error) {
NSLog(#"error adding album");
}];
Now i want to save the recorded videos to this 999Videos album created.Not to the photosAlbum which i have done like this.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputFileURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
completionBlock:^(NSURL *assetURL, NSError *error)
The videos are saving but not in the 999Videos album.Could someone please tell me how can i save the videos to my custom album?
After tearing my hair out over this finally i found the solution.Here is my code.
NSString *albumName=#"999 Videos";
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:albumName
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"added album:%#", albumName);
}
failureBlock:^(NSError *error) {
NSLog(#"error adding album");
}];
__block ALAssetsGroup* groupToAddTo;
[library enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:albumName]) {
NSLog(#"found album %#", albumName);
groupToAddTo = group;
}
}
failureBlock:^(NSError* error) {
NSLog(#"failed to enumerate albums:\nError: %#", [error localizedDescription]);
}];
[library assetForURL:assetURL
resultBlock:^(ALAsset *asset) {
// assign the photo to the album
[groupToAddTo addAsset:asset];
NSLog(#"Added %# to %#", [[asset defaultRepresentation] filename], albumName);
}
failureBlock:^(NSError* error) {
NSLog(#"failed to retrieve image asset:\nError: %# ", [error localizedDescription]);
}];

How to know image present at the url

I have url for image picked using ELCImagePickerController. I stored the url for future reference.
I get that URL using:
[dict valueForKey:UIImagePickerControllerReferenceURL];
Now the problem arises when after some time user deleted that particular image from photo library
and I am going to access that image using URL.
My app does not get crashed.
I have tried using NSUrl method
[imagePath checkResourceIsReachableAndReturnError:&err]
as well i tried something like:
-(BOOL)findImage:(NSURL*)path
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block BOOL flag=YES;
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
if (iref) {
flag=YES;
dispatch_group_leave(group);
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"cant get image - %#",[myerror localizedDescription]);
flag=NO;
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:path resultBlock:resultblock failureBlock:failureblock];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[assetslibrary release];
return flag;
}
Sample url :
assets-library://asset/asset.JPG?id=E862927E-E646-448A-9EB6-A7D48668B3DC&ext=JPG
But no success.
How to know that image present at particular URL.
If any one can help me out on this will be appreciable.
Thanks in advance.
For this case you need to check ALAssetRepresentation *rep = [myasset defaultRepresentation] to nil.
if(rep != nil){
//write your code..
}
Solved the problem with change in the findImage method
-(BOOL)findImage:(NSURL*)path
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block BOOL flag=YES;
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:path resultBlock:^(ALAsset *asset) {
if (asset==nil)
{
flag=NO;
}
else
{
flag=YES;
}
dispatch_group_leave(group);
} failureBlock:^(NSError *error){
NSLog(#"operation was not successfull!");
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[assetslibrary release];
return flag;
}

SLRequest performRequestWithHandler does not work in ios 6

I am trying to build simple prototype where I post some text to my facebook account. I have read the ios 6 facebook integration documentation and came up with following code. Everything seems to work fine until I hit the last block where I create SLRequest object in method postTextToFacebook and try to execute performRequestWithHandler with handler block. Control never does inside the handler block. I am assuming that performRequestWithHandler call is not successful in this case. Any one have done with successfully? Here is code for your reference.
#import <Social/Social.h>
#import "ViewController.h"
#implementation ViewController
#synthesize facebookAccount;
#synthesize accountStore;
#synthesize textToPost;
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(IBAction) postToFacebook:(id)sender
{
self.statusLabel.text = #"Logging in ...";
if(self.accountStore == nil)
{
self.accountStore = [[ACAccountStore alloc] init];
}
ACAccountType *facebookAccountType = [self.accountStore enter code hereaccountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSMutableDictionary *myOptions = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"172197712918906", ACFacebookAppIdKey,
[NSArray arrayWithObjects:#"email", #"user_about_me", #"user_likes", nil], ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:myOptions completion:^(BOOL granted, NSError *error){
__block NSString *statusText;
if(granted)
{
NSArray *accounts = [self.accountStore accountsWithAccountType:facebookAccountType];
self.facebookAccount = [accounts lastObject];
[myOptions setObject:[NSArray arrayWithObjects:#"publish_stream", nil] forKey:ACFacebookPermissionsKey];
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:myOptions completion:^(BOOL granted, NSError *error) {
__block NSString *statusText1;
if(granted && error == nil)
{
NSArray *accounts = [self.accountStore accountsWithAccountType:facebookAccountType];
self.facebookAccount = [accounts lastObject];
[self postTextToFacebook];
statusText1 = #"Text Posted.";
}
else{
statusText1 = #"Publish Failed.";
}
dispatch_async(dispatch_get_main_queue(), ^{
self.statusLabel.text = statusText1;
});
}];
}
else{
statusText = #"Login Failed.";
NSLog(#"Error = %#",error);
}
}];
}
-(void) postTextToFacebook
{
NSDictionary *parameters = #{#"message":self.textToPost.text};
NSURL *feedURL = [NSURL URLWithString:#"https://graphs.facebook.com/me/feed"];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodPOST URL:feedURL parameters:parameters];
request.account = self.facebookAccount;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(#"Facebook request received, status code %d", urlResponse.statusCode);
NSString *response = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"Response data: %#", response);
//handle errors
if(error == nil)
{
dispatch_async(dispatch_get_main_queue(), ^{
self.statusLabel.text = #"text posted to facebook";
});
}
}];
}
#end
Your url should be
https://graph.facebook.com/me/feed
instead of
https://graphs.facebook.com/me/feed
I was having a same issue and got an NSURLErrorDomain -1003 error on it.

How to check if an ALAsset still exists using a URL

I've got an array containing ALAsset urls (not full ALAsset objects)
So each time I start my application I want to check my array to see if it's still up to date...
So I tried
NSData *assetData = [[NSData alloc] initWithContentsOfFile:#"assets-library://asset/asset.PNG?id=1000000001&ext=PNG"];
but assetData is allways nil
thx for the help
Use assetForURL:resultBlock:failureBlock: method of ALAssetsLibrary instead to get the asset from its URL.
// Create assets library
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
// Try to load asset at mediaURL
[library assetForURL:mediaURL resultBlock:^(ALAsset *asset) {
// If asset exists
if (asset) {
// Type your code here for successful
} else {
// Type your code here for not existing asset
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
}];
Having assets path you can use this function to check if image exist:
-(BOOL) imageExistAtPath:(NSString *)assetsPath
{
__block BOOL imageExist = NO;
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:assetsPath] resultBlock:^(ALAsset *asset) {
if (asset) {
imageExist = YES;
}
} failureBlock:^(NSError *error) {
NSLog(#"Error %#", error);
}];
return imageExist;
}
Remember that checking if image exist is checking asynchronyus.
If you want to wait until new thread finish his life call function "imageExistAtPath" in main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self imageExistAtPath:assetPath];
});
Or you can use semaphores, but this is not very nice solution:
-(BOOL) imageExistAtPath:(NSString *)assetsPath
{
__block BOOL imageExist = YES;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:assetsPath] resultBlock:^(ALAsset *asset) {
if (asset) {
dispatch_semaphore_signal(semaphore);
} else {
imageExist = NO;
dispatch_semaphore_signal(semaphore);
}
} failureBlock:^(NSError *error) {
NSLog(#"Error %#", error);
}];
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return imageExist;
}
For iOS 8 or later, there is a synchronous method to check if an ALAsset exists.
#import Photos;
if ([PHAsset fetchAssetsWithALAssetURLs:#[assetURL] options:nil].count) {
// exist
}
Swift:
import Photos
if PHAsset.fetchAssetsWithALAssetURLs([assetURL], options: nil).count > 0 {
// exist
}
Swift 3:
import Photos
if PHAsset.fetchAssets(withALAssetURLs: [assetURL], options: nil).count > 0 {
// exist
}
Use this method to check if file exists
NSURL *yourFile = [[self applicationDocumentsDirectory]URLByAppendingPathComponent:#"YourFileHere.txt"];
if ([[NSFileManager defaultManager]fileExistsAtPath:storeFile.path
isDirectory:NO]) {
NSLog(#"The file DOES exist");
} else {
NSLog(#"The file does NOT exist");
}

Problem passing NSError back as a return parameter

I am having a problem passing an NSError object back. The first line of code to access the object (in this case, I inserted an NSLog) causes "EXC_BAD_ACCESS".
Is this because I am not explicitly creating an NSError object, but rather getting one from the NSURLRequest and passing it back? In this particular function (downloadFile:), some errors I want to retrieve from other functions, but I create an NSError on two other occasions in the function.
Any help is appreciated.
Here is the offending code:
-(void)someCode {
NSError *err = nil;
localPool = [[NSAutoreleasePool alloc] init];
if (!iap) {
iap = [[InAppPurchaseController alloc] init];
}
if (![self.iap downloadFile:#"XXXXX.plist" withRemoteDirectory:nil withLocalDelete:YES withContentType:#"text/xml" Error:&err] ) {
//"EXC_BAD_ACCESS" on calling NSLog on the next line?
NSLog(#"Error downloading Plist: %#", [err localizedDescription]);
[self performSelectorOnMainThread:#selector(fetchPlistFailed:) withObject:err waitUntilDone:NO];
[localPool drain], localPool = nil;
return NO;
}
//Removed the remainder of the code for clarity.
[localPool drain], localPool = nil;
return YES;
}
-(BOOL)downloadFile:(NSString *)fileName
withRemoteDirectory:(NSString *)remoteDirectory
withLocalDelete:(BOOL)withLocalDelete
withContentType:(NSString *)contentTypeCheckString
Error:(NSError **)error {
UIApplication *app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
NSError *localError = nil;
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
NSString *urlString = [NSString stringWithFormat:#"http://XXXXX/%#", fileName];
NSLog(#"Downloading file: %#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&localError];
[req release];
if (response == nil || localError) {
NSLog(#"Error retrieving file:%#", [localError localizedDescription]);
if (error != NULL) {
*error = localError;
//THIS NSLog call works just fine.
NSLog(#"Error copied is:%#", [*error localizedDescription]);
}
[localPool drain], localPool = nil;
app.networkActivityIndicatorVisible = NO;
return NO;
}
//Rest of function omitted for simplicity.
}
I guess your NSError object is autoreleased and put on your localPool. You drained that localPool, thus destroying the NSError.
Do you really need localPool in every method? If not, just remove the localPools.
Also, it looks like you forgot to drain the localPool in someCode. Hopefully you just didn't copy it...
-(void)someCode {
NSError *err = nil;
localPool = [[NSAutoreleasePool alloc] init];
if (!iap) {
iap = [[InAppPurchaseController alloc] init];
}
if (![self.iap downloadFile:#"XXXXX.plist" withRemoteDirectory:nil withLocalDelete:YES withContentType:#"text/xml" Error:&err] ) {
....
[localPool drain], localPool = nil;
return NO;
}
[localPool drain], localPool = nil; // missing
}