i want to use async req to get json data, i am doing this by syncrouns already, but now requirement is change, but i am unable to modify this code to aync, beacuse i have to return NSdata
+ (NSString *)stringWithUrl:(NSURL *)url
{
// if(kShowLog)
NSLog(#"%#", url);
NSURL *newURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#",url]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:newURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:1];
// Fetch the JSON response
NSData *urlData;
NSURLResponse *response;
NSError *error;
// NSOperationQueue *opQueue;
// Make synchronous request
urlData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
return [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
}
call stringWithUrl on a 2. thread if you want to keep returning the data. (I'd go with async data
dispatch_async(dispatch_get_global_queue(), ^{
NSData *d = [self stringWithUrl:myURL];
...
//update ui
dispatch_sync(dispatch_get_main_queue(), ^{
...
});
});
a way that would work but is QUITE the hack (which apple employed in older APIs on the mac all the time) would be to run the runloop while waiting:
hackisch :: but wraps async in sync given the completion block runs on the same thread then the runloop
__block NSString *responseString = nil;
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:myurl]
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
responseString = [[NSString alloc] initWithData:Data encoding:NSUTF8StringEncoding];
if(!responseString) {
responseString = #"";
}
}];
while(!responseString) {
[NSRunloop currentRunloop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1];
}
return responseString;
You have to create a separate thread for this process, Synchronous call of getting json data will block your main thread.
I'm changing your code to do asynchronous operation for getting json data from web service.
+ (void)stringWithUrl:(NSURL *)url
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
// if(kShowLog)
NSLog(#"%#", url);
NSURL *newURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#",url]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:newURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:1];
// Fetch the JSON response
NSData *urlData;
NSURLResponse *response;
NSError *error;
// NSOperationQueue *opQueue;
// Make synchronous request
urlData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
//Here you have to put your code, which you wanted to get executed after your data successfully retrived.
//This block will get executed after your request data load to urlData.
});
});
}
What's wrong with the Asynchronous then,
NSString *responseString;
NSOperationQueue *operation = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:operation completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"data %#", data);
responseString = [[NSString alloc] initWithData:Data encoding:NSUTF8StringEncoding];
}];
return responseString;
Related
//Below is the code i have used to perform simple httpPOST. But app hangs on App launch on splash screen and crashes . i am doing an API Call on applaunch in Appdelegate
- (NSDictionary *)postUserRegWithUrl:(NSString *)urlString andParams:(NSString *)paramString{
NSString * encodedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)paramString,
NULL,
(CFStringRef)#"+",
kCFStringEncodingUTF8 ));
NSDictionary *responseDictionary;
NSError *err;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#",kBaseProdURL,urlString]]];
NSData *data = [encodedString dataUsingEncoding:NSUTF8StringEncoding];
[request setTimeoutInterval:60.0];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:data];
NSLog(#"the data Details is =%#", request);
NSURLResponse *response;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSString *resSrt = [[NSString alloc]initWithData:responseData encoding:NSASCIIStringEncoding];
NSLog(#"got response==%#", resSrt);
if(resSrt.length)
{
responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&err];
NSLog(#"Response dictionary formed is =%#", responseDictionary);
} else {
NSLog(#"failed to connect");
[self showAlertViewTitle:#"Please try later" withMessage:#"Something went wrong"];
}
return responseDictionary;
}
You shouldn't execute your network calls synchronously, especially on main thread. Either use sendAsynchronousRequest or just use any good networking library, like AFNetworking, which do this out of the box.
First set the timeoutInterval for your request. if your request takes more time then you have to stop the api call and inform the user with proper error message.
For example:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1200.0];
Don't use Synchronised Request. It will block your main thread.
If your network is slow or server is not responding then your app will take more time to load. Which is not good for the user experience.
Remember, your app’s load time is your first chance to impress your users.
Use Asynchronised Request of the NSURLConnection. Handle the response in the api completion block.
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSString *resSrt = [[NSString alloc]initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"got response==%#", resSrt);
if(resSrt.length)
{
responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&err];
NSLog(#"Response dictionary formed is =%#", responseDictionary);
} else {
NSLog(#"failed to connect");
}
}];
Change queue:[NSOperationQueue mainQueue] parameter based on your need.
queue -> An NSOperationQueue upon which the handler block will
be dispatched.
I have a method which send request to server and i have created the call to that function with completionHandler as follows:
-(void)sendAddSubscriptionRequest:(NSString*)owner withComppletionHandler:(void (^)(BOOL, NSArray *, NSError *))completion
dataWebService = [[NSMutableData alloc] init];
NSURL* aUrl = [NSURL
URLWithString:[NSString stringWithFormat:#"https://www.google.com/add?"]];
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[request addValue:[NSString stringWithFormat:#"Bearer %#", "token"]
forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"POST"];
NSString* postData =
[[NSString alloc] initWithFormat:#"owner=%#",
owner];
[request setHTTPBody:[postData dataUsingEncoding:NSUTF8StringEncoding]];
NSData* returnData = [NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
};
My problem is how should i return completion handler to my function after the data is fetched.
Thanks,.
As you are sending an asynchronous request, the method will return immediately as the data will be "returned" in the NSURLConnection completion handler. You can then decode it there and pass it along to your own completion handler:
[NSURLConnection sendAsynchronousRequest:request
queue:nil
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSArray *array = nil;
if (!connectionError)
array = /* get from data somehow */
completion(!connectionError, array, connectionError);
}];
Note: It's not clear what your completion handler parameters mean, so I might have taken some liberties.
I've got this api (http://www.timeapi.org/utc/now) that just gives the time as a string and I want to use NSURLConnection to retrieve it, except I'm confused as to how NSURLConnection works.
Current code:
+(NSString *) fetchTime
{
NSString *timeString=#"not_set";
//Code for URL request here
NSURL *timeURL = [NSURL URLWithString:#"http://www.timeapi.org/utc/now"]
return timeString;
}
The method is called from the view controller that will then in turn display it on the screen as per MVC, all I need is a good example to get me in the right direction.
In order to make a request to that api you need something like this:
NSURL *timeURL = [NSURL URLWithString:#"http://www.timeapi.org/utc/now"]
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:120];
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
NSString *string = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
What you want to do is send an Asynchronous request to the server to fetch the Time. If you send out a synchronous request it will block your UI and for some reason if server took a minute to send the response back user wont be able to do anything for a minute. Example using the standard API :
Note that if you are using sync request you can expect a return value but in async calls you would need the help of a block to return the value. So
-(void) fetchTimeFromServerWithCompletionHandler:(void(^)(id)) onComplete {
NSURLRequest *timeRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.timeapi.org/utc/now"]];
[NSURLConnection sendAsynchronousRequest:timeRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *urlResponse, NSData *data, NSError *error) {
// Do something usefull with Data.
// If expected object is a String, alloc init a String with received Data
NSString *time = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
onComplete(time); // This will return back the time string.
}];
}
If you are using service API a lot in your app, you can check out AFNetworking as well.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
I am creating an asynchronous NSURLconnection in a popup view in ios.
To implement the asynchronous NSURLconnection I implement the methods of the NSURLDelegate.
The problem occurs when the user taps outside the popup view and the view is dismissed.
leaving the nsurlconnection callbacks and other actions inside the view incomplete.
How can I assure that the actions inside the popup complete inspite of the dismissal of the view?
I tried putting an activity indicator inside the popup view till the actions are completed, but even then a tap outside the popup view dismisses the view.
I dont want the user to be left with an inactive app till actions are completed, instead I want the actions to be completed in the background.
If you want to send an asynchronous connection you can use this methods.
GET REQUEST
-(void)placeGetRequest:(NSString *)action withHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *error))ourBlock {
NSString *url = [NSString stringWithFormat:#"%#/%#", URL_API, action];
NSURL *urlUsers = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:urlUsers];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:ourBlock];
}
POST REQUEST
-(void)placePostRequest:(NSString *)action withData:(NSDictionary *)dataToSend withHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *error))ourBlock {
NSString *urlString = [NSString stringWithFormat:#"%#/%#", URL_API, action];
NSLog(urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// Creamos el JSON desde el data
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dataToSend options:0 error:&error];
NSString *jsonString;
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSData *requestData = [NSData dataWithBytes:[jsonString UTF8String] length:[jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:ourBlock];
}
}
EXAMPLE OF USE
- (void) getMyMethod:(NSString *)myParam1
myParam2:(NSString *)myParam2
myParam3:(NSString *)myParam3
calledBy:(id)calledBy
withSuccess:(SEL)successCallback
andFailure:(SEL)failureCallback{
[self placeGetRequest:[NSString stringWithFormat:#"api/myMethod?myParam1=%#&myParam2=%#&myParam3=%#",myParam1, myParam2, myParam3]
withHandler:^(NSURLResponse *response, NSData *rawData, NSError *error) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSInteger code = [httpResponse statusCode];
NSLog(#"%ld", (long)code);
if (code == 0){
// error
} else if (!(code >= 200 && code < 300) && !(code == 500)) {
NSString *string = [[NSString alloc] initWithData:rawData
encoding:NSUTF8StringEncoding];
NSLog(#"ERROR (%ld): %#", (long)code, string);
[calledBy performSelector:failureCallback withObject:string];
} else {
// If you receive a JSON
NSMutableDictionary *result = [NSJSONSerialization JSONObjectWithData:rawData options:0 error:nil];
// If you receive an Array
// NSArray *result = [NSJSONSerialization JSONObjectWithData:rawData options:0 error:nil];
// If you receive a string
// NSString *result = [[NSString alloc] initWithData:rawData encoding:NSUTF8StringEncoding];
[calledBy performSelector:successCallback withObject:result];
}
}];
}
CALL YOU MUST DO IN YOUR VIEW/CONTROLLER/ETC
(...)
[api getMyMethod:myParam1Value myParam2:myParam2Value myParam3:myParam3Value calledBy:self withSuccess:#selector(getMyMethodDidEnd:) andFailure:#selector(getMyMethodFailureFailure:)];
(...)
// Don't forget to set your callbacks functions or callbacks will do your app crash
-(void)getMyMethodDidEnd:(id)result{
// your actions with the result
// ...
}
-(void)getMyMethodFailure:(id)result{
// your actions with the result
// ...
}
To prevent the dismissal of popup view when tapping out side u need to implement this delegate method
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return NO;
}
dismiss it by using the action
- (void)someAction
{
//check the operations are completed
.....
.....
[popoverController dismissPopoverAnimated:YES];
}
UPDATE: Here's all the file's I'm using in my github repo. Perhaps it will help more than my code snippets:
appDelegate
mainViewController
calendarHandler
NSString* result = [NSString stringWithContentsOfURL:urlRequest encoding:NSUTF8StringEncoding error:&err];
That's the line I'm using in my project to get data from a website. I run this in an NSOperationQueue and when I run with my app in the foreground, it works without issue. However I also have my app set to run in the background using performFetchWithCompletionHandler. All of my setup code works fine using the performFetch, but when it hits the line I've outlined above, the app just hangs until I bring my app into the foreground again.
How can I let this work in the background?
In my AppDelegate I have performFetchWithCompletionHandler:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
id topViewController = navigationController.topViewController;
if ([topViewController isKindOfClass:[ViewController class]])
{
[(ViewController*)topViewController autoLogin];
}
completionHandler(UIBackgroundFetchResultNewData);
}
My NSOperationQueue is built like this in my MainViewController:
- (void) autologin
{
NSOperationQueue* backgroundQueue = [NSOperationQueue new];
ch = [[myEventHandler alloc] init];
NSInvocationOperation* operation = [[NSInvocationOperation alloc] initWithTarget:ch selector:#selector(runEvents:) object:login];
[backgroundQueue addOperation:operation];
}
And in MyEventHandler I have runEvents
- (void) runEvents
{
NSURL* urlRequest = [NSURL URLWithString:#"http://www.google.com"];
NSError* err = nil;
NSString* result = [NSString stringWithContentsOfURL:urlRequest encoding:NSUTF8StringEncoding error:&err];
}
Try with This code:
NSString *urlString=[NSString stringWithFormat:#"%#listcontact.php?uid=%#&page=0",LocalPath,appdel.strid];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSError *error1;
NSDictionary *res=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error1];
}];
have you tried using NSURLConnection (asynchronous) instead of stringWithContentsOfURL (synchronous)?
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
[NSURLConnection sendAsynchronousRequest:request queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSString *theResult = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// blah blah
}];