I have a singleton class where i have implemented a method to parse json data through URL. The code is as below
-(id)parseJsonDataWIthURL:(NSString *)url :(NSString*)datumm
{
NSMutableDictionary *arrrrr=[[NSMutableDictionary alloc]init];
NSMutableURLRequest *reqqq=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:url]];
NSData *dataaa=[datumm dataUsingEncoding:NSUTF8StringEncoding];
[reqqq setHTTPMethod:#"POST"];
[reqqq setHTTPBody:dataaa];
NSURLSessionConfiguration *configg=[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession*sessionn=[NSURLSession sessionWithConfiguration:configg delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *taskk=[sessionn dataTaskWithRequest:reqqq completionHandler:^(NSData *data,NSURLResponse *responce,NSError *error){
if(error)
{
NSLog(#"%#", [error localizedDescription]);
}else{
NSMutableDictionary *d = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
NSLog(#"data %#",d);
[arrrrr setDictionary:d];
}
}];
[taskk resume];
return arrrrr;
}
The method returns no values, it is because the blocks takes time to execute within that method returns the result. So is there any way to wait until block completes and return the value.
ragul ml,
Simplest solution I can suggest is to use blocks :)
Here is how you can achieve it :) modify -(id)parseJsonDataWIthURL:(NSString *)url :(NSString*)datumm to,
-(void)parseJsonDataWIthURL:(NSString *)url :(NSString*)datumm withCompletionBlock:(void(^)(NSMutableArray *))completionBlock
{
NSMutableDictionary *arrrrr=[[NSMutableDictionary alloc]init];
NSMutableURLRequest *reqqq=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:url]];
NSData *dataaa=[datumm dataUsingEncoding:NSUTF8StringEncoding];
[reqqq setHTTPMethod:#"POST"];
[reqqq setHTTPBody:dataaa];
NSURLSessionConfiguration *configg=[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession*sessionn=[NSURLSession sessionWithConfiguration:configg delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *taskk=[sessionn dataTaskWithRequest:reqqq completionHandler:^(NSData *data,NSURLResponse *responce,NSError *error){
if(error)
{
NSLog(#"%#", [error localizedDescription]);
completionBlock(nil)
}else{
NSMutableDictionary *d = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
NSLog(#"data %#",d);
[arrrrr setDictionary:d];
if (completionBlock) {
completionBlock(arrrrr);
}
}
}];
[taskk resume];
}
finally modify your call as,
[[YourSingletonClass sharedInstance] parseJsonDataWIthURL:your_url :datumm withCompletionBlock:^(NSMutableArray *array) {
if (array){
//web service is successful
}
}];
Related
I'm trying to test a NSJSONSerialization with this URL ; the link works, and the app runs, but when I log the serialization, I get a null result.
Can anyone tell me what I'm doing wrong? Code:
#implementation ViewController
- ( void )viewDidLoad
{
[super viewDidLoad];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"https:franciscocosta.net/lisbon-spots"]];
NSURLSessionDataTask *task = [[self getURLSession] dataTaskWithRequest:request completionHandler:^( NSData *data, NSURLResponse *response, NSError *error )
{dispatch_async( dispatch_get_main_queue(),
^{
NSError *jsonError;
NSArray *parsedJSONArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
NSLog( #"%#", parsedJSONArray );
} );
}];
[task resume];
}
- ( NSURLSession * )getURLSession
{
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once( &onceToken,
^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration];
} );
return session;
}
It looks like your JSON is not well-formed.
If type your link into Safari, then copy the resulting JSON and paste it into a JSON verifier like JSONLint, it shows an error on line 25. It looks like there is closing bracket (]) that doesn't belong there. It appears that that JSON contains 2 root array objects, which I don't think is valid.
If you refactor it to contain a single array of dictionaries, it passes the JSONLint test as valid JSON.
I am new to Objective C and iOS development in general. I am trying to create an app that would make an http request and display the contents on a label.
When I started testing I noticed that the label was blank even though my logs showed that I had data back. Apparently this happens because the the response is not ready when the label text gets updated.
I put a loop on the top to fix this but I am almost sure there's got to be a better way to deal with this.
ViewController.m
- (IBAction)buttonSearch:(id)sender {
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: #"https://en.wiktionary.org/wiki/incredible"];
//I put this here to give some time for the url session to comeback.
int count;
while (http.responseText ==nil) {
self.outputLabel.text = [NSString stringWithFormat: #"Getting data %i ", count];
}
self.outputLabel.text = http.responseText;
}
HttpRequest.h
#import <Foundation/Foundation.h>
#interface HttpRequest : NSObject
#property (strong, nonatomic) NSString *responseText;
- (void) sendRequestFromURL: (NSString *) url;
- (NSString *) getElementBetweenText: (NSString *) start andText: (NSString *) end;
#end
HttpRequest.m
#implementation HttpRequest
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
}];
[task resume];
}
Thanks a lot for the help :)
Update
After reading a lot for the very useful comments here I realized that I was missing the whole point. So technically the NSURLSessionDataTask will add task to a queue that will make the call asynchronously and then I have to provide that call with a block of code I want to execute when the thread generated by the task has been completed.
Duncan thanks a lot for the response and the comments in the code. That helped me a lot to understand.
So I rewrote my procedures using the information provided. Note that they are a little verbose but, I wanted it like that understand the whole concept for now. (I am declaring a code block rather than nesting them)
HttpRequest.m
- (void) sendRequestFromURL: (NSString *) url
completion:(void (^)(NSString *, NSError *))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Create a block to handle the background thread in the dispatch method.
void (^runAfterCompletion)(void) = ^void (void) {
if (error) {
completionBlock (nil, error);
} else {
NSString *dataText = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
completionBlock(dataText, error);
}
};
//Dispatch the queue
dispatch_async(dispatch_get_main_queue(), runAfterCompletion);
}];
[task resume];
}
ViewController.m
- (IBAction)buttonSearch:(id)sender {
NSString *const myURL = #"https://en.wiktionary.org/wiki/incredible";
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: myURL
completion: ^(NSString *str, NSError *error) {
if (error) {
self.outputText.text = [error localizedDescription];
} else {
self.outputText.text = str;
}
}];
}
Please feel free to comment on my new code. Style, incorrect usage, incorrect flow; feedback is very important in this stage of learning so I can become a better developer :)
Again thanks a lot for the replies.
You know what, use AFNetworking to save your life.
Or just modify your HttpRequest's sendRequestFromURL:
- (void)sendRequestFromURL:(NSString *)url completion:(void(^)(NSString *str, NSError *error))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
completionBlock(nil, error);
} else {
completionBlock([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], error);
}
});
}];
[task resume];
}
and invoke like this
[http sendRequestFromURL:#"https://en.wiktionary.org/wiki/incredible" completion:^(NSString *str, NSError *error) {
if (!error) {
self.outputLabel.text = str;
}
}];
Rewrite your sendRequestFromURL function to take a completion block:
- (void) sendRequestFromURL: (NSString *) url
completion: (void (^)(void)) completion
{
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
if (completion != nil)
{
//The data task's completion block runs on a background thread
//by default, so invoke the completion handler on the main thread
//for safety
dispatch_async(dispatch_get_main_queue(), completion);
}
}];
[task resume];
}
Then, when you call sendRequestFromURL, pass in the code you want to run when the request is ready as the completion block:
[self.sendRequestFromURL: #"http://www.someURL.com&blahblahblah",
completion: ^
{
//The code that you want to run when the data task is complete, using
//self.responseText
}];
//Do NOT expect the result to be ready here. It won't be.
The code above uses a completion block with no parameters because your code saved the response text to an instance variable. It would be more typical to pass the response data and the NSError as parameters to the completion block. See #Yahoho's answer for a version of sendRequestFromURL that takes a completion block with a result string and an NSError parameter).
(Note: I wrote the code above in the SO post editor. It probably has a few syntax errors, but it's intended as a guide, not code you can copy/paste into place. Objective-C block syntax is kinda nasty and I usually get it wrong the first time at least half the time.)
If you want easy way then Don't make separate class for call webservice. Just make meethod in viewController.m instead. I mean write sendRequestFromURL in your viewController.m and update your label's text in completion handler something like,
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
self.outputLabel.text = self.responseText;
})
}];
[task resume];
}
I am creating an app that uses Google Place. Before I used to use Yahoo's api and had to use a url that was responsible for local search that was provided by yahoo. The url was following:
http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=SF0DVEvV34G4GnXEDU4SXniaDebJ_UvC1G1IuikVz3vpOJrBpyD.VqCJCVJHMh99He3iFz1Rzoqxb0b7Z.0-
Now since yahoo's api is discontinued I have decided to switch over to Google Place. But I cannot find an Url to use. I just dowlod the framework and use the api key. Where can I find such url for Google Place.
Register for the Google Places API by following the linke provided below:
https://code.google.com/apis/console
Refer Code Link for places Auto Search
https://github.com/AdamBCo/ABCGooglePlacesAutocomplete
NSString *const apiKey = #"*****23xAHRvnOf2BVG8o";
NSString * searchWord = #"search some place "
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&radius=500&language=en&key=%#",searchWord,apiKey];
pragma mark - Network Methods
-(void)retrieveGooglePlaceInformation:(NSString *)searchWord withCompletion:(void (^)(BOOL isSuccess, NSError *error))completion {
if (!searchWord) {
return;
}
searchWord = searchWord.lowercaseString;
self.searchResults = [NSMutableArray array];
if ([self.searchResultsCache objectForKey:searchWord]) {
NSArray * pastResults = [self.searchResultsCache objectForKey:searchWord];
self.searchResults = [NSMutableArray arrayWithArray:pastResults];
completion(YES, nil);
} else {
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&radius=500&language=en&key=%#",searchWord,apiKey];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jSONresult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
if (error || [jSONresult[#"status"] isEqualToString:#"NOT_FOUND"] || [jSONresult[#"status"] isEqualToString:#"REQUEST_DENIED"]){
if (!error){
NSDictionary *userInfo = #{#"error":jSONresult[#"status"]};
NSError *newError = [NSError errorWithDomain:#"API Error" code:666 userInfo:userInfo];
completion(NO, newError);
return;
}
completion(NO, error);
return;
} else {
NSArray *results = [jSONresult valueForKey:#"predictions"];
for (NSDictionary *jsonDictionary in results) {
}
//[self.searchResultsCache setObject:self.searchResults forKey:searchWord];
completion(YES, nil);
}
}];
[task resume];
}
}
I am trying to create a NSString function to return a string that has the NSURLSessionDataTask
- (NSString *) retrieveData
{
self.session = [NSURLSession sharedSession];
self.dataTask = [self.session dataTaskWithURL:[NSURL URLWithString:URL] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if(data)
{
self.json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(#"%#", self.json);
for(NSDictionary *stateArray in self.json)
{
NSString *sName = stateArray[#"State"];
if(self.state == sName)
{
NSString *sFlag = stateArray[#"State_Flag_Path"];
return sFlag;
}
}
}
else
{
NSLog(#"Failed to fetch URL: %#", error);
}
}];
[self.dataTask resume];
}
Then, I get an error message for incompatible block pointer types issue.
Can anyone please help?
Thank you in advance.
I found a way...
return the string after
[self.dataTask resume];
In my app i need to call two services at a time. for single service i am using the below code:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// Instantiate a session object.
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *url = [NSURL URLWithString:#"my link"];
// Create a data task object to perform the data downloading.
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error != nil) {
// If any error occurs then just display its description on the console.
NSLog(#"%#", [error localizedDescription]);
}
else{
// If no error occurs, check the HTTP status code.
NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode];
// If it's other than 200, then show it on the console.
if (HTTPStatusCode != 200) {
NSLog(#"HTTP status code = %d", (int)HTTPStatusCode);
} else {
NSMutableArray *jsonData = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves error:nil];
NSLog(#"json data ==========> %#", jsonData);
}
}
}];
// Resume the task.
[task resume];
by using this i am getting the data. Now, at the same time i need to call another service. How can i achieve this? and How i will get the data?