Facing an issue with NSURLSessionDataTask with SynchronousRequest in objective-c - objective-c

Here is my working code with NSURLConnection sendSynchronousRequest :
+ (Inc*)getData:(NSString*)inUUID {
NSString* urlString = [NSString stringWithFormat:#"/inc/%#", incUUID];
NSURLRequest* request = [[HttpRequest requestWithRelativePath:urlString] toNSMutableURLRequest];
NSDictionary* json = [self getJSONForRequest:request];
return [Inc incFromDictionary:json];
}
+ (NSDictionary*)getJSONForRequest:(NSURLRequest*)request {
NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
}
But, sendSynchronousRequest:request is deprecated.
So that, I used NSURLSessionDataTaskinstead of sendSynchronousRequest. Here, is the code which I implemented:
+ (Inc*)getData1:(NSString*)inUUID {
NSString* urlString = [NSString stringWithFormat:#"/in/%#", inUUID];
NSURLRequest* request = [[HttpRequest requestWithRelativePath:urlString] toNSMutableURLRequest];
//NSDictionary* json = [self getJSONForRequest1:request];
__block NSDictionary* json;
dispatch_async(dispatch_get_main_queue(), ^{
[self getJsonResponse1:request success:^(NSDictionary *responseDict) {
json = [responseDict valueForKeyPath:#"detail"];;
//return [Inc incFromDictionary:json];
} failure:^(NSError *error) {
// error handling here ...
}];
});
return [Inc incFromDictionary:json];
}
+ (void)getJsonResponse1:(NSURLRequest *)urlStr success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSessionDataTask *dataTask1 = [[NSURLSession sharedSession] dataTaskWithRequest:urlStr completionHandler:^(NSData *data, NSURLResponse *response,
NSError *error) {
NSLog(#"%#",data);
if (error)
failure(error);
else {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#",json);
success(json);
}
}];
[dataTask1 resume]; // Executed First
}
The problem is return statement call in getData1 before finish the api call.
Thanks in advance.

As mentioned in the comments you need a completion handler.
Something like this (untested):
+ (void)getData1:(NSString*)inUUID success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure {
NSString* urlString = [NSString stringWithFormat:#"/in/%#", inUUID];
NSURLRequest* request = [[HttpRequest requestWithRelativePath:urlString] toNSMutableURLRequest];
[self getJsonResponse1:request success:^(NSDictionary *responseDict) {
NSDictionary* json = [responseDict valueForKeyPath:#"detail"];
success(json);
} failure:^(NSError *error) {
failure(error);
}];
}
+ (void)getJsonResponse1:(NSURLRequest *)urlStr success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSessionDataTask *dataTask1 = [[NSURLSession sharedSession] dataTaskWithRequest:urlStr completionHandler:^(NSData *data, NSURLResponse *response,
NSError *error) {
NSLog(#"%#",data);
if (error)
failure(error);
else {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#",json);
success(json);
}
}];
[dataTask1 resume]; // Executed First
}
And to call
[MyClass getData1:#"asdf" success:^(NSDictionary *responseDict) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *json = [responseDict valueForKeyPath:#"detail"];
Inc *inc = [Inc incFromDictionary:json];
// do something witrh `inc`
});
} failure:^(NSError *error) {
// error handling here ...
}];
Consider to use instances and instance methods of your class(es) rather than only class methods.

Related

Asynchronously Api calls and returning data outside the block

I don't understand why I am getting null array outside the block code, even though I am using __block keyword on my array.
I am successfully getting data from a backend api with following code
`-(void)getJsonResponse:(NSString *)urlStr success:(void (^)(NSArray *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:urlStr];
// Asynchronously API is hit here
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// NSLog(#"%#",data);
if (error)
failure(error);
else {
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// NSLog(#"%#",json);
success(json);
}
}];
[dataTask resume]; // Executed First
}`
Then in my function for returning the data I am using following
`- (NSArray *)get_data:(NSDictionary *)credentials{
NSString *urlStr =[ NSString stringWithFormat:#"http://test.com %#",credentials];
__block NSArray *jsonArray= [[NSArray alloc]init];
[self getJsonResponse:urlStr success:^(NSArray *responseArray) {
jsonArray = responseArray;
NSLog(#"%#",responseArray);
} failure:^(NSError *error) {
// error handling here ...
}];
NSLog(#"%#",jsonArray);
return jsonArray;
}
`
The issue here is although I am successfully getting data within getJsonResponse block, but when I am trying to return the response data array as function return I am getting null for jsonArray. I thought assigning __block infront of jsonArray should retain the data assign within the block code ?
The second approach is not to use Async way like following
`- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
{
NSError __block *err = NULL;
NSData __block *data;
BOOL __block reqProcessed = false;
NSURLResponse __block *resp;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) {
resp = _response;
err = _error;
data = _data;
reqProcessed = true;
}] resume];
while (!reqProcessed) {
[NSThread sleepForTimeInterval:0];
}
*response = resp;
*error = err;
return data;
}`
That way its blocking the main thread whilst waiting for data.
I would suggest using the same approach of getJsonResponse for your get_data function:
- (void)get_data:(NSDictionary *)credentials finish:(void(^)(NSArray *data))finish{
NSString *urlStr =[ NSString stringWithFormat:#"http://test.com %#",credentials];
__block NSArray *jsonArray= [[NSArray alloc]init];
[self getJsonResponse:urlStr success:^(NSArray *responseArray) {
jsonArray = responseArray;
if (finish) {
finish(jsonArray);
}
} failure:^(NSError *error) {
// error handling here ...
}];
}

Getting error while fetching data from server using NSURLSession Datatask in objective c

I was trying to load data for the table using values from server.I am using NSURLSession datatask with completion handler. Whenever it reaches the nsurlsession, it shows error.This is the code which i used for getting data.
- (void)geturl:(NSString *)urlvalue datavalues:(NSString *)string fetchGreetingcompletion:(void (^)(NSDictionary *dictionary, NSError *error))completion{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#?%#",common.getUrlPort,urlvalue,common.getappversion]];
NSLog(#"url=%#",url);
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[urlRequest addValue:common.getauthtoken forHTTPHeaderField:#"Authorization"];
//Create POST Params and add it to HTTPBody
[urlRequest setHTTPMethod:#"GET"];
NSURLSessionDataTask * dataTask =[defaultSession dataTaskWithRequest:urlRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
NSLog(#"Response:%# %#\n", response, connectionError);
if (data.length > 0 && connectionError == nil)
{
NSDictionary *greeting = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSString *code = [NSString stringWithFormat:#"%#",[greeting valueForKey:#"code"]];
if([code isEqualToString:#"-1"]){
[self loaderrorview:greeting];
}
else{
if (completion)
completion(greeting, connectionError);
}
}
else if(data == nil){
NSDictionary *errorDict=[[NSDictionary alloc]initWithObjectsAndKeys:#"Server Connection Failed",#"error", nil];
if (completion)
completion(errorDict,connectionError);
}
else
{
NSDictionary *greeting = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if (completion)
completion(greeting, connectionError);
}
}];
[dataTask resume];
}
The code which i used for getting data from server:
-(void)getdataexplore{
if (!common.checkIfInternetIsAvailable) {
[self.view makeToast:Nointernetconnection];
} else {
NSLog(#"There is internet connection");
[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];
[SVProgressHUD showWithStatus:#"Loading..."];
[apiservice geturl:loadexploredata datavalues:nil fetchGreetingcompletion:^(NSDictionary *dictionary, NSError *error) {
//NSLog(#"Test %# Error %#",dictionary,error);
if(error == nil){
authDictionary = dictionary;
[self loaddata];
}
else{
[SVProgressHUD dismiss];
[view_business makeToast:#"Request timed out" duration:2.0 position:CSToastPositionCenter];
}
}];
}
}
The code which i used for storing server data to array:
-(void)loaddata
{
[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];
[SVProgressHUD showWithStatus:#"Loading..."];
//[SVProgressHUD dismiss];
NSString *msg = [authDictionary valueForKey:#"msg"];
NSString *code = [NSString stringWithFormat:#"%#",[authDictionary valueForKey:#"code"]];
if([code isEqualToString:#"201"]){
NSDictionary *explore = [authDictionary valueForKey:#"explore_obj"];
arr_CBcategories = [explore valueForKey:#"cb_categories"];
[common setarrCBCaterory:arr_CBcategories];
arr_CBcategoryid = [arr_CBcategories valueForKey:#"id"];
[common setarrCateroryID:arr_CBcategoryid];
arr_CBcategorytitle = [arr_CBcategories valueForKey:#"title"];
[common setarrCaterorytitle:arr_CBcategorytitle];
arr_CBcategoryslug = [arr_CBcategories valueForKey:#"slug"];
[common setarrCateroryslug:arr_CBcategoryslug];
arr_CBcategoryimage = [arr_CBcategories valueForKey:#"image"];
[common setarrCateroryimage:arr_CBcategoryimage];
arr_CBcategorycode = [arr_CBcategories valueForKey:#"code"];
}
I am getting error like "Unable to run main thread". Any solution for this.

how I can Modify this code

I'm using this function to send a request to a server and receive JSON response in an NSDictionary.
- (void)performRequest:(NSString *)aRequest
{
NSString *string=[NSString stringWithFormat:#"%#%#",baseURL,aRequest];
NSURL *url = [NSURL URLWithString: string];
NSLog(#"string%#",string);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary * greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
NSLog(#"%#",greeting2);
}
}];
}
What I want is to modify it so that it remains a function sending service URL and a separate one where the JSON response comes at a NSDictionary so you can reuse them in other classes.
I used the networking introduced by iOS 7: NSURLSession. This is the networking code I use in a project. But I would suggest you to use AFNetworking instead. That will save you more time on it. It depends on what you want in your application.
+ (void)requestOperationWithMethod:(NSString *)method
withUrl:(NSString *)url
header:(NSDictionary *)header
params:(NSDictionary *)params
completion:(FHCompletionResultBlock)completion {
FHNetworking *network = [FHNetworking shareNetworking];
NSData *bodyData;
NSError *jsonError = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:params options:kNilOptions error:&jsonError];
if (!jsonError) {
bodyData = data;
}
// prepare http request header and body data fields
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
for (NSString *key in header.allKeys) {
[request setValue:header[key] forHTTPHeaderField:key];
}
[request setHTTPMethod:method];
[request setHTTPBody:bodyData];
[request setTimeoutInterval:30.0f];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[[network.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
if (jsonError)
completion(nil, jsonError);
else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode >= 400) {
if (json[#"code"] && json[#"error"]) {
NSError *error = [NSError errorWithDomain:#"Error" code:[json[#"code"] integerValue] userInfo:#{NSLocalizedDescriptionKey: NSLocalizedString(json[#"error"], nil)}];
completion(nil, error);
}
else {
NSError *error = [NSError errorWithDomain:#"Error" code:-1 userInfo:#{NSLocalizedDescriptionKey: NSLocalizedString(#"Unknown error", nil)}];
completion(nil, error);
}
}
else {
completion(json, nil);
}
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}] resume];
}
You can change your method to take a completionHandler parameter, itself:
- (void)performRequest:(NSString *)aRequest completionHandler:(void(^)(NSDictionary *responseObject, NSError *error))completionHandler
{
NSString *string=[NSString stringWithFormat:#"%#%#",baseURL,aRequest];
NSURL *url = [NSURL URLWithString: string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError) {
if (data.length > 0 && connectionError == nil) {
NSError *parseError;
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&parseError];
completionHandler(responseObject, parseError);
} else {
completionHandler(nil, error);
}
}];
}
Now you can call it and supply whatever you want in the completion handler:
[self performRequest:requestString completionHandler:^(NSDictionary *responseObject, NSError *error) {
if (responseObject) {
// everything is good; use your NSDictionary here
} else {
// handle error here
}
}];

Making stringWithContentsOfURL asynchronous - Is it safe?

I attempted to make -[NSString stringWithContentsOfURL:encoding:error:] asynchronous, by running it a-synchronically from a background thread:
__block NSString *result;
dispatch_queue_t currentQueue = dispatch_get_current_queue();
void (^doneBlock)(void) = ^{
printf("done! %s",[result UTF8String]);
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
result = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com/"] encoding:NSUTF8StringEncoding error:nil];
dispatch_sync(currentQueue, ^{
doneBlock();
});
});
Its working fine, and most importantly, its asynchronous.
My question is if it's safe to do this, or could there be any threading problems etc.?
Thanks in advance :)
That should be safe, but why reinvent the wheel?
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// etc
}];
You can also use:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSString *searchResultString = [NSString stringWithContentsOfURL:[NSURL URLWithString:searchURL]
encoding:NSUTF8StringEncoding
error:&error];
if (error != nil) {
completionBlock(term,nil,error);
}
else
{
// Parse the JSON Response
NSData *jsonData = [searchResultString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *searchResultsDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions
error:&error];
if(error != nil)
{
completionBlock(term,nil,error);
}
else
{
//Other Work here
}
}
});
But yes, it should be safe. I've been told though to use NSURLConnection instead due to error calls and such when communicating via the internet. I'm still doing research into this.
-(void)loadappdetails:(NSString*)appid {
NSString* searchurl = [#"https://itunes.apple.com/lookup?id=" stringByAppendingString:appid];
[self performSelectorInBackground:#selector(asyncload:) withObject:searchurl];
}
-(void)asyncload:(NSString*)searchurl {
NSURL* url = [NSURL URLWithString:searchurl];
NSError* error = nil;
NSString* str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (error != nil) {
NSLog(#"Error: %#", error);
}
NSLog(#"str: %#", str);
}

Returning NSDictionary from async code block? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
returning UIImage from block
Hi I'm trying to return dictionary of json twitter data so i can use it in my application. How ever it is being called from a async block. I can not save it or return it any thoughts?
-(NSDictionary *)TweetFetcher
{
TWRequest *request = [[TWRequest alloc] initWithURL:
[NSURL URLWithString: #"http://search.twitter.com/search.json?
q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"] parameters:nil
requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse
*urlResponse,
NSError *error)
{
if ([urlResponse statusCode] == 200)
{
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData
options:0 error:&error];
//resultsArray return an array [of dicitionaries<tweets>];
NSArray* resultsArray = [dict objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
NSLog([NSString stringWithFormat:#"%#", [internalDict
objectForKey:#"from_user_name"]]);
----> return dict; // i need this dictionary of json twitter data
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
Thnx in advance!
I feel like I've written a ton of this async code lately.
- (void)tweetFetcherWithCompletion:(void(^)(NSDictionary *dict, NSError *error))completion
{
NSURL *URL = [NSURL URLWithString:#"http://search.twitter.com/search.json?q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"];
TWRequest *request = [[TWRequest alloc] initWithURL:URL parameters:nil requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
if (error) {
completion(nil, error);
return;
}
//resultsArray return an array [of dicitionaries<tweets>];
NSArray* resultsArray = [dict objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
NSLog(#"%#", [internalDict objectForKey:#"from_user_name"]);
completion(dict, nil);
}
else {
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
completion(nil, error);
}
}];
}
So, instead of calling self.tweetDict = [self TweetFetcher];, you would call it this way.
[self tweetFetcherWithCompletion:^(NSDictionary *dict, NSError *error) {
if (error) {
// Handle Error Somehow
}
self.tweetDict = dict;
// Everything else you need to do with the dictionary.
}];