I am calling webservice on mapView drag/regionChange. I want to keep some delay in the web service calls. So I want that whenever the user drags the map multiple times, all previous web service calls should be cancelled and only last drag web service call should be fired.
How do I do this?
Following is my code:
{ .... NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSString *postLength = [NSString stringWithFormat:#"%lu",(unsigned long)[data length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:data];
[request setHTTPMethod:#"POST"];
NSMutableDictionary __block *dictResponse;
[[NSOperationQueue mainQueue] cancelAllOperations];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if(connectionError == nil){
dictResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&connectionError];
}
You cannot cancel a sendAsynchronousRequest. If you used a delegate-based NSURLConnection request, then you could cancel it, but (a) that's more coding than you probably want to bother with; (b) NSURLConnection is deprecated, so you should be NSURLSession anyway; and (c) NSURLSession allows you to cancel the task using the NSURLSessionTask reference that is returned to you:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setHTTPBody:data];
[request setHTTPMethod:#"POST"];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"connection error = %#", error);
return;
}
NSError *parseError;
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&parseError];
if (!responseObject) {
NSLog(#"parse error = %#", parseError);
}
dispatch_async(dispatch_get_main_queue(), ^{
// use response here; e.g., updating UI or model objects
});
}];
[task resume];
If you need to cancel this request, just call [task cancel]. So save this NSURLSessionTask in some weak variable if you want to keep track of it and cancel it later.
Don't use the sendAsynchronousRequest convenience API, use the main delegate API so you have access to the instance of NSURLConnecyion to call cancel on.
Also, don't cancel operations on the main queue, you don't know what's there, and consider using NSURLSession
Related
Hi in my application i am using DISPATCH_TIME_FOREVER for ever API call. It seems it blocking the main thread when network is slow.I don't have much idea about this DISPATCH_TIME_FOREVER. Can anyone guide me.Here is the code which i am using.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *postDic=[[NSMutableDictionary alloc]init];
[postDic setObject:#"getProfileDetails" forKey:#"cmd"];
[postDic setObject:[[NSUserDefaults standardUserDefaults]objectForKey:#"accountnumber"] forKey:#"accountNumber"];
[postDic setObject:[[NSUserDefaults standardUserDefaults]objectForKey:#"deviceID"] forKey:#"deviceId"];
NSData *postData;
dispatch_group_t group=dispatch_group_create();
if(postDic!=nil)
postData=[NSJSONSerialization dataWithJSONObject:postDic options:0 error:nil];
NSString *postLength=[NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
// remote ip
NSString *remoteAddress=[NSString stringWithFormat:#"%#",[[NSUserDefaults standardUserDefaults]objectForKey:#"remoteIP"]];
NSURL *url=[NSURL URLWithString:remoteAddress];
NSMutableURLRequest *request =[[NSMutableURLRequest alloc]init];
[request setURL:url];
[request setHTTPMethod:#"Post"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:[NSString stringWithFormat:#"%# %#",[[NSUserDefaults standardUserDefaults] valueForKey:#"tokenType"],[[NSUserDefaults standardUserDefaults] valueForKey:#"accessToken"]] forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setTimeoutInterval:10.0];
[request setHTTPBody:postData];
dispatch_group_enter(group);
NSURLSessionConfiguration *profileConfiguration=[NSURLSessionConfiguration defaultSessionConfiguration];
profileConfiguration.timeoutIntervalForResource=30.0;
profileConfiguration.timeoutIntervalForRequest=30.0;
NSURLSession *profileSession=[NSURLSession sessionWithConfiguration:profileConfiguration];
NSURLSessionDataTask *profileTask=[profileSession dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
{
NSLog(#"profile Error is %#",error);
NSDictionary* lineUpStationsResponse;
if(data!=nil)
profileResponse=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
// If result is null then send empty dictionary back to viewcontroller to display that login customer not subscribed to any service
if(error!=nil)
{
completionBlock(postDic,nil,error);
}
if([NSJSONSerialization isValidJSONObject:lineUpStationsResponse])
{
completionBlock(postDic,profileResponse,error);
}
else
{
NSDictionary *resultDic=[[NSDictionary alloc]init];
completionBlock(postDic,resultDic,error);
}
// NSLog(#"profile is %#",profileResponse);
dispatch_group_leave(group);
}];
[profileTask resume];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
});
I have
NSString* FBAuthoValue= #"TESTINGCONSTANT";
On click of Load Button ,it call HitLoadAPI
I am calling my API like below HitLoadAPI, now if at my server end my FBAuthoValue is change i need to Hit another API to get refresh value of FBAuthoValue, and set in HitLoadAPI.
1) User hit HitLoadAPI with FBAuthoValue= #"TESTINGCONSTANT" value, but as in server now FBAuthoValue= #"NewTestCode", so it return httpresponsecode 909,on 909 i need to call refreshFBAuthValue api, and put this value back to HitLoadAPI, and the api work correctly.
2) if FBAuthoValue token change in server, need to call refreshFBAuthValue API, and its return value need to set and call the HitLoadAPI again,without knowing the user.
NOTE: i have to hit multiple API one after another, suppose API-1,API-2,API-3 and so on, and if FBAuthoValue, if in any api ,changes in server then need to refresh that FBAuthoValue and then the same API need to be call, without effecting or blocking to user.
I will reward 50 bounty for sure.
Overview: API-1 call, in the meanwhile if token expire,need to call token expire api, and the API-1 will recall again, without user press Load Button again.
Here is my code
-(void)HitLoadAPI
{
NSError *error;
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:#"[JSON SERVER"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setHTTPMethod:#"POST"];
[request setValue:FBAuthoValue forHTTPHeaderField:FBAUTH];
NSDictionary *mapData = [[NSDictionary alloc] initWithObjectsAndKeys: #"TEST IOS", #"name",
#"IOS TYPE", #"typemap",
nil];
NSData *postData = [NSJSONSerialization dataWithJSONObject:mapData options:0 error:&error];
[request setHTTPBody:postData];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary* headers = [(NSHTTPURLResponse *)response allHeaderFields];
//NSLog(#" headers =%#",headers);
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
//NSLog(#"response status code: %ld", (long)[httpResponse statusCode]);
if([httpResponse statusCode]==909)
{
FBAuthoValue =[self refreshFBAuthValue];
//what to do here so the current API hit will be call again....
}
}];
[postDataTask resume];
}
-(NSString *) refreshFBAuthValue
{
//hit api to get new refresh token code here need its calling code as well as the block coding cause it response so late which cause return value nil...to HitLoadAPI
return FBaccess_token; //it will return refresh FBaccess_token code
}
You can create a method which you can use to call any api from anywhere in the application, This method takes the parameter specific to an api call, like api url, data which will be included in the body of the request and a completion block which will be called when fbAuthValue is valid.
-(void)HitAPILoadWithFbAuthValue:(NSString*)fbAuthValue apiUrl:(NSString*)apiUrl postData:(NSDictionary*)dict withCompletion:(void (^)(int statusCode,NSURLResponse * apiResponse,NSError * error))completion{
NSError *error;
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:apiUrl];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setHTTPMethod:#"POST"];
[request setValue: fbAuthValue forHTTPHeaderField:FBAUTH];
//post body with dictionary passed as a parameter
NSData *postData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error];
[request setHTTPBody:postData];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary* headers = [(NSHTTPURLResponse *)response allHeaderFields];
//NSLog(#" headers =%#",headers);
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
//NSLog(#"response status code: %ld", (long)[httpResponse statusCode]);
if([httpResponse statusCode]==909)
{
FBAuthoValue =[self refreshFBAuthValue];
//what to do here so the current API hit will be call again....
if (FBAuthoValue){
[self HitAPILoadWithFbAuthValue:fbAuthValue apiUrl:apiUrl postData:dict withCompletion:completion];
}else{
}
}
//if FbAuthValue is valid, call completion block
completion((int)[httpResponse statusCode],response, error);
}];
[postDataTask resume];
}
Usage
I assume you write this method in a separate class called APIManager. So to call this method first create an instance of the APIManager and call like this:
APIManager *sharedManager = [APIManager sharedInstance]
//call API_1
[sharedManager HitAPILoadWithFbAuthValue:FBAuthValue apiUrl:#"API_1_URL" postData:dict_for_first_api withCompletion:^(int statusCode, NSURLResponse *apiResponse, NSError *error) {
if(error != nil){
//handle error here
}else{
//call API_2
[sharedManager HitAPILoadWithFbAuthValue:FBAuthValue apiUrl:#"API_2_URL" postData:dict_for_second_api withCompletion:^(int statusCode, NSURLResponse *apiResponse, NSError *error) {
}];
}
}];
i am working on Google cloud endpoint with datastore. When i am passing data through Android & iOS with method POST , it is getting saved in datastore with empty record. i.e. request is properly working but the way i am sending data is wrong. please help me for this.
For iOS, code is mentioned below :
NSDictionary *headers = #{ #"content-type": #"application/json",
#"cache-control": #"no-cache",
#"postman-token": #"404012ff-722e-c5d8-48db-fa7fb3260841"};
NSLog(#"header %#",headers);
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"enter url here..."]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSString* strRequest=#"";
strRequest =[[NSString alloc] initWithFormat:#"displayEmail=%#&displayName=%#",#"sonal#gmail.com",#"sonal"];
NSLog(#"url is %# parameter = %#", request , strRequest);
[request setHTTPMethod:#"POST"];
[request setHTTPBody: [strRequest dataUsingEncoding:NSUTF8StringEncoding]];
[request setAllHTTPHeaderFields:headers];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"responce Body===>%#",responseBody);
if (error) {
NSLog(#"%#", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(#"responce%#", httpResponse);
}
}];
[dataTask resume];
You need to set the httpBody property of your request. If you don't provide a body for the POST, of course it's blank.
https://developer.apple.com/reference/foundation/nsmutableurlrequest/1409064-httpbody
Or Objective-C
https://developer.apple.com/reference/foundation/nsmutableurlrequest/1409064-httpbody?language=objc
We got a solution..
we changed the way of passing data..
here is code :
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:syncURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
NSData *requestData = [NSData dataWithBytes:[serverSyncAPIRequestJSON UTF8String] length:[serverSyncAPIRequestJSON length]];[request setHTTPMethod:#"POST"]; [request setHTTPBody:requestData]; [request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
Using uber rush API I've successfully created authorization token. But when I try to create quote all I get is HTML page. I am making http request from iOS Objective C. This is how I am making http request:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#/v1/deliveries/quote", kLiveURL]]];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"Bearer %#", [[NSUserDefaults standardUserDefaults] objectForKey:kUberRushAccessTokenKey]] forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%lul", (unsigned long)[jsonData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:jsonData];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data) {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error) {
NSLog(#"dataString: %#",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
} else {
NSLog(#"jsonResponse: %#", jsonResponse);
}
} else {
NSLog(#"httpResponse: %#", response);
}
}];
Thanks for help in advance :)
I'm currently trying to execute a method after a NSURLSession completes. The problem is I can't manage in any way the asynchronous nature of its object. I've tried with GCD and NSOperation as suggested in the other questions but nothing changes: after the initialization with dataTaskWithRequest:completionHandeler:, the application starts executing the next method in the program.
Here is the method which realizes the networking:
-(void)sendData{
NSData *JSONdata = [NSJSONSerialization dataWithJSONObject:userInfoToJSON options:0 error:&error];
NSURL *url = [NSURL URLWithString:#"http://mobdev2015.com/register.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[JSONdata length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:JSONdata];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
requestReply = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}]resume];
});
}
Here is the method instead which calls the one above and where I want to implement a synchronous execution:
-(void)MethodWhichNeedsToBeSync{
//Creating a JSON object..
[self sendData:userInfoToJSON];
//MethodB wants to be executed if and only if sendData is completed
[self MethodB];
}
Thank you for the replies.
The way to execute a method after the completion of an asynchronous method is to have that method called in the completion block (or closure) of the asynchronous method.
For your case, it would look something like this:
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
requestReply = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
if (!error) {
[self MethodB];
}
}]resume];
});
where the call to be run after successful completion of the asynchronous method is moved into the completion handler.
That would accomplish the execution of MethodB if and only if sendData is completed, as you wanted.
As far as I can tell from the NSURLSession documentation there's no way to use it to make synchronous requests. I assume that's because you really shouldn't be making calls that could block the main queue.
However, if using NSURLSession isn't required you can simply use NSURLConnection instead. You would initialize it with your request and call "sendSynchronousRequest:returningResponse:".