Calling a GCD Block else where in Main Thread - iphone

I am using following Code to get response from a string query. There are a lot of queries all around in my app and i want to copy and paste this code again and again
Is there any way I can just make an instance of it , pass the urlString and then return the response..
I have tried creating a function
+(NSString*) myFunc{} in an NSObject Class but it seems that GCD Doesn't work except Main UI Threads. How can i fix this issue
__block__ NSString *response;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//your server url and request. data comes back in this background thread
response; = [NSString stringWithContentsOfURL:[NSURL URLWithString:queryString] encoding:NSUTF8StringEncoding error:&err];
dispatch_async(dispatch_get_main_queue(), ^{
//update main thread here.
NSLog(#"%#",response); // NEED TO RETURN THIS
if (err != nil)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle: #"Error"
message: #"An error has occurred."
delegate: self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
[indicator stopAnimating];
}
});
});

I would separate out the request processing from the error reporting, using a completion block to provide the feedback to the caller.
First define the completion block semantics; we know we want the string response and an optional error descriptor:
typedef void (^COMPLETION_BLOCK)(NSString *response, NSString *error);
Second implement the method that will get the response in the background and then call the completion block in the main thread. This could be a class method in some global utility class if you wish:
- (void)responseFromURL:(NSURL *)url
completionBlock:(COMPLETION_BLOCK)completionBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSError *error = nil;
NSString *response = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(response, error);
}
}
}
And finally call the method:
[someClass responseFromURL:[NSURL URLWithString:queryString]
completionBlock:^(NSString *response, NSError *error) {
NSLog(#"response='%#'", response);
if (error != nil)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error getting response"
message:[error localizedDescription]
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
[indicator stopAnimating];
}
}];
(this code is untested and will therefore doubtless contain some bugs)

Related

Downloading Files in queue using AFNetworking in iPhone

I need to download multiple files in queue. Right now my code is working and all files are downloading simultaneously, However i need to download one file at a time, and all other files are in queue, below is code, please let me know what i doing wrong. I just need to download one file at a time and all other file should be in queue.
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:videoURL];
[httpClient.operationQueue setMaxConcurrentOperationCount:1];
for (NSURL *videoString in videoArray) {
NSURLRequest *request = [NSURLRequest requestWithURL:videoString];
AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:path shouldResume:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if(operation.response.statusCode == 200) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Successfully Downloaded" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if(operation.response.statusCode!= 200) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Error While Downloaded" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}];
[httpClient enqueueHTTPRequestOperation:operation];
[operation setProgressiveDownloadProgressBlock:^(NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
float percentDone = ((float)totalBytesRead) / totalBytesExpectedToReadForFile;
}
}];
To limit the queue to one Operation at a time,
you could try adding dependencies between each operation before you queue them.
Like this
[Operation2 addDependency:Operation1];
Hope that helps!
use dispatch queues(GCD) after the block setCompletionBlockWithSuccess:
like following way:
dispatch_async(dispatch_get_main_queue(), ^{
// Call any method from on the instance that created the operation here.
[self doSomework]; // example
});

Stop the nextprocess when network connection fails

I'm taking xml response from service url and I want to display error, when network connection fails. So I display the UIAlertView, but this alertView is getting displayed after the rest of process is completed. I want it to be shown immediately.
In android, if network connection fails, it will display an error alert that "Unfortunately app name has terminated". Is there anything of such for iPhone? If not I want to show alertview and stop the rest of the process.
This is the code I'm working on:
if (responseData!= NULL)
{
response = [[NSString alloc] initWithData:responseData
encoding:NSUTF8StringEncoding ];
NSLog(#"Response Code:%d",[urlResponse statusCode]);
if([urlResponse statusCode ]>=200 && [urlResponse statusCode]<300)
{
NSLog(#"Response:%#",response);
}
}
else
{
NSLog(#"Failed to send request: %#", [error localizedDescription]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unfornately stopped.Try Again " message:#"" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
parser =[[NSXMLParser alloc]initWithData:responseData];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
currentHtmlElement=#"1";
[parser parse];
[parser release];
In my code if it fails to send request, then NSLog gets printed and alertView code is executed. At the same time I want to stop the rest of the process i.e initialising a parser and doing the rest of operation.
How can I do it?
Very simple... Move your parse code inside if block.
if (responseData!= NULL)
{
response = [[NSString alloc] initWithData:responseData
encoding:NSUTF8StringEncoding ];
NSLog(#"Response Code:%d",[urlResponse statusCode]);
if([urlResponse statusCode ]>=200 && [urlResponse statusCode]<300)
{
NSLog(#"Response:%#",response);
}
parser =[[NSXMLParser alloc]initWithData:responseData];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
currentHtmlElement=#"1";
[parser parse];
[parser release];
}
else
{
NSLog(#"Failed to send request: %#", [error localizedDescription]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unfornately iCloudBiz has stopped.Try Again " message:#"" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
By the way... You mentioned your Android's app behavior as follows
In android if network connection fails it will display an error alert that Unfortunatly app name has terminated.
Please understand that this behavior is an abnormal behavior. This is called a CRASH and you should handle this appropriately. If network is disconnected app should display a message without terminating/crashing :)
You should use Reachability class to check for whether internet connection is available or not
this is sample code to understand how it works
Reachability *reachability = [Reachability reachabilityForInternetConnection];
NetworkStatus internetStatus = [reachability currentReachabilityStatus];
if (internetStatus != NotReachable) {
//my web-dependent code
}
else {
//there-is-no-connection warning
}

UIAlertView on main queue crashing.

I have an issue showing a UIAlertView on the main thread. I'm not sure why but it keeps crashing, despite me running on the main thread. The following block is on the background thread, but I have the alert on the main as below:
void (^removeFromCalendar)(NSString *, NSString *, EKEventStore *) = ^(NSString *error, NSString *eventKey, EKEventStore *eventDB) {
EKEvent *myEvent = [eventDB eventWithIdentifier:eventKey];
NSError *err = noErr;
if(myEvent != NULL && myEvent != (id)[NSNull null]) {
[eventDB removeEvent:myEvent span:EKSpanThisEvent error:&err];
} else {
// Event was not found, nothing to do
return;
}
[eventDB release];
if (!err || err == noErr) {
NSLog(#"Deleted event %#", myEvent.title);
// Show alert on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Showing the alert for unattending
NSString *resultString = #"This event was removed from your calendar.";
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Info" message:resultString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] autorelease];
[alert show];
});
return;
}
error = err.description;
};
If I comment out the bottom where it shows the alert, everything is fine. But for the alert, I keep getting a EXC_BAD_ACCESS error. Can somebody explain why? It's on the correct thread, and I cant for the life of me understand where the memory issue could come from!
May be you view is being released when you finish until you finish with the background queue. So, for safety why dont you use it like this;
...........
UIViewController __weak *myController = self;
dispathch_async(backgroundQueue, ^{
UIViewController __strong *myStrongController = myController;
...............
dispatch_async(dispatch_get_main_queue(), ^{
if(myStrongController){
// Showing the alert for unattending
NSString *resultString = #"This event was removed from your calendar.";
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Info" message:resultString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] autorelease];
[alert show];
}
});
}).
This is how you present an alert view:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"<#(NSString *)#>" message:#"<#(NSString *)#>" delegate:self cancelButtonTitle:#"<#(NSString *)#>" otherButtonTitles:nil];
[alert show];
[alert release];
Instead of using dispatch_async, why not use the objective C call:
[self performSelectorOnMainThread
You might have to package it up in its own method. Alternatively, call it using:
[self performSelector:#selector(myAlertMethod) withObject:nil afterDelay:0.25]
These methods have been tried and true since day 1.

Facebook sdk post on wall on iPhone app

I have a problem with implementing Facebook posting on wall in my iPhone application.
I installed SDK and linked framework
login is working fine. here's the code:
-(IBAction)loginButtonPressed:(id)sender
{
NSLog(#"loginButtonPressed: called");
AppDelegate *appdel=[[UIApplication sharedApplication] delegate];
appdel.facebookSession=[[FBSession alloc] init];
[appdel.facebookSession openWithCompletionHandler:^(FBSession *session,
FBSessionState status,
NSError *error)
{
//
}];
}
But I have a problem with posting message on user's wall. Here's the code:
-(IBAction)likeButtonPressed:(id)sender
{
NSLog(#"likeButtonPressed: called");
// Post a status update to the user's feedm via the Graph API, and display an alert view
// with the results or an error.
NSString *message = #"test message";
NSDictionary *params = [NSDictionary dictionaryWithObject:message forKey:#"message"];
// use the "startWith" helper static on FBRequest to both create and start a request, with
// a specified completion handler.
[FBRequest startWithGraphPath:#"me/feed"
parameters:params
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
[self showAlert:message result:result error:error];
}];
}
Help me please. What's wrong with my code? Or should I add some permissions to login request?
this code worked for me.
First we must
#import <FBiOSSDK/FacebookSDK.h>
then
#property (strong, nonatomic) FBRequestConnection *requestConnection;
and of course do not forget to synthesize:
#synthesize requestConnection;
the code itself:
-(IBAction)likeButtonPressed:(id)sender
{
NSLog(#"likeButtonPressed: called");
// FBSample logic
// Check to see whether we have already opened a session.
if (FBSession.activeSession.isOpen)
{
// login is integrated with the send button -- so if open, we send
[self postOnWall];
}
else
{
[FBSession sessionOpenWithPermissions:[NSArray arrayWithObjects:#"publish_stream", nil]
completionHandler:
^(FBSession *session,
FBSessionState status,
NSError *error)
{
// if login fails for any reason, we alert
if (error)
{
NSLog(#" login failed");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
// if otherwise we check to see if the session is open, an alternative to
// to the FB_ISSESSIONOPENWITHSTATE helper-macro would be to check the isOpen
// property of the session object; the macros are useful, however, for more
// detailed state checking for FBSession objects
}
else if (FB_ISSESSIONOPENWITHSTATE(status))
{
NSLog(#" sending post on wall request...");
// send our requests if we successfully logged in
[self postOnWall];
}
}];
};
}
- (void)postOnWall
{
NSNumber *testMessageIndex=[[NSNumber alloc] init];
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"testMessageIndex"]==nil)
{
testMessageIndex=[NSNumber numberWithInt:100];
}
else
{
testMessageIndex=[[NSUserDefaults standardUserDefaults] objectForKey:#"testMessageIndex"];
};
testMessageIndex=[NSNumber numberWithInt:[testMessageIndex intValue]+1];
[[NSUserDefaults standardUserDefaults] setObject:testMessageIndex forKey:#"testMessageIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
// create the connection object
FBRequestConnection *newConnection = [[FBRequestConnection alloc] init];
// create a handler block to handle the results of the request for fbid's profile
FBRequestHandler handler =
^(FBRequestConnection *connection, id result, NSError *error) {
// output the results of the request
[self requestCompleted:connection forFbID:#"me" result:result error:error];
};
// create the request object, using the fbid as the graph path
// as an alternative the request* static methods of the FBRequest class could
// be used to fetch common requests, such as /me and /me/friends
NSString *messageString=[NSString stringWithFormat:#"wk test message %i", [testMessageIndex intValue]];
FBRequest *request=[[FBRequest alloc] initWithSession:FBSession.activeSession graphPath:#"me/feed" parameters:[NSDictionary dictionaryWithObject:messageString forKey:#"message"] HTTPMethod:#"POST"];
// add the request to the connection object, if more than one request is added
// the connection object will compose the requests as a batch request; whether or
// not the request is a batch or a singleton, the handler behavior is the same,
// allowing the application to be dynamic in regards to whether a single or multiple
// requests are occuring
[newConnection addRequest:request completionHandler:handler];
// if there's an outstanding connection, just cancel
[self.requestConnection cancel];
// keep track of our connection, and start it
self.requestConnection = newConnection;
[newConnection start];
}
// FBSample logic
// Report any results. Invoked once for each request we make.
- (void)requestCompleted:(FBRequestConnection *)connection
forFbID:fbID
result:(id)result
error:(NSError *)error
{
NSLog(#"request completed");
// not the completion we were looking for...
if (self.requestConnection &&
connection != self.requestConnection)
{
NSLog(#" not the completion we are looking for");
return;
}
// clean this up, for posterity
self.requestConnection = nil;
if (error)
{
NSLog(#" error");
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
// error contains details about why the request failed
[alert show];
}
else
{
NSLog(#" ok");
};
}
try this code
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* facebookCookies = [cookies cookiesForURL:
[NSURL URLWithString:#"http://login.facebook.com"]];
for (NSHTTPCookie* cookie in facebookCookies) {
[cookies deleteCookie:cookie];
}
NSString *FBBody = [NSString stringWithFormat:#"your message you want to post"];
UIImage *img=[UIImage imageNamed:[NSString stringWithFormat:image naemif you want to post]];
FBFeedPost *post = [[FBFeedPost alloc] initWithPhoto:img name:FBBody];
[post publishPostWithDelegate:self];
[[UIAppDelegate indicator] startAnimating];
IFNNotificationDisplay *display = [[IFNNotificationDisplay alloc] init];
display.type = NotificationDisplayTypeLoading;
display.tag = NOTIFICATION_DISPLAY_TAG;
you should set Permissions:"status_update".
like this :
FBLoginView *loginview = [[FBLoginView alloc] initWithPermissions:[NSArray arrayWithObject:#"status_update"]];
or
FBSession *fbSession = [[FBSession alloc] initWithPermissions:[NSArray arrayWithObject:#"status_update"]];

Resolving the wait situation for a detached thread in IPhone?

I am using a private MBProgressHUD
Now I am using the indicator view on my add button in which I am calling my addrecord service .
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
// Add HUD to screen
[window addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
the adding to favorites method :
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
[request setHTTPMethod:#"GET"];
//[request setTimeoutInterval:10];
//NSURLResponse *response = nil;
// NSError *error = nil;
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *data1= [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
if(data1 == nil)
{
doneFlag = NO;
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert"
message:#"The network is not available.\n Please check the Internet connection."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
doneFlag = YES;
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Confirmation"
message:#"Added To favorites"
delegate:nil
cancelButtonTitle:#"OKAY"
otherButtonTitles:nil];
[alert show];
alert = nil;
[alert release];
}
[request release];
This is all running fine except the instruments gives leak of the uialertview may be it is conflicting with the mbprogreshud.
So I thought to remove the alert from the calling method and put it in the caller the method like this:
the caller method now :
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
// Add HUD to screen
[window addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
//it should wait for the above line to be executing ******* then to exexute the be //below condition but how ?
if (doneFlag == NO) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert"
message:#"The network is not available.\n Please check the Internet connection."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
} else {
[favoritesButton setTitle:#"Remove" forState:UIControlStateNormal];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Confirmation"
message:#"Added To favorites"
delegate:nil
cancelButtonTitle:#"OKAY"
otherButtonTitles:nil];
[alert show];
alert = nil;
[alert release];
}
the adding to favorites method :
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
[request setHTTPMethod:#"GET"];
//[request setTimeoutInterval:10];
//NSURLResponse *response = nil;
// NSError *error = nil;
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *data1= [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
if(data1 == nil)
{
doneFlag = NO;
}
else
{
doneFlag = YES;
}
[request release];
In the launching of the progresshud thread is detaching something like this :
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil]
Now My question is that If I follow the first scenario . How can I assure the the alertview leak will not come
Or If I am following the second scenario How can I assure the if condition will be executed after completing this line executed :
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
Regarding the first scenario, it is in general a bad idea to do UI updates from threads other than the applications main thread. UIKit is NOT thread safe and doing threaded UI updates can cause all sorts of strange things to happen. Now, I'm not sure if this is the cause for the leak but I would avoid showing an UIAlertView in addingToFavorites. Use performSelectorOnMainThread or the second scenario described below.
Regarding the second scenario, move everything below the showWhileExecuting call to the hudWasHidden delegate method. At this point you can be sure that your code was fully executed and the doneFlag was set.
To use performSelectorOnMainThread, define a new method, put your code in it and than call performSelectorOnMainThread.
I.e.,
- (void)showAlert {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"The network is not available.\n Please check the Internet connection." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
Call with,
[self performSelectorOnMainThread:#selector(showAlert) withObject:nil waitUntilDone:NO];
I would go with the second scenario though.
Other answers notwithstanding, you were creating the UIAlertView leak with this sequence:
[alert show];
alert = nil;
[alert release];
The last two lines should be swapped:
[alert show];
[alert release];
alert = nil;