Objective-C - URL Session to get data for table view - ios

I have an iOS app with a table view and I want to get the table items from an extern php file which gives me a json string. Now I try to store the data from the php file to the data variable. Here is my code:
- (NSMutableArray *)data {
if(!_data){
_data = [[NSMutableArray alloc] init];
NSURLSession *session = [NSURLSession sharedSession];
NSString *url = [NSString stringWithFormat:#"http://www.example.net/something/bla.php"];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *errorCode = [json valueForKey:#"error"];
if([errorCode isEqualToString:#"e0"]){
NSArray *persons = [json objectForKey:#"persons"];
for (int i = 0; i < [persons count]; i++) {
NSString *person = [[persons objectAtIndex:i] objectForKey:#"name"];
[_data addObject:person];
}
}
NSLog(#"3.: %#", _data); // Full
}];
[dataTask resume];
NSLog(#"2.: %#", _data); // Empty
}
NSLog(#"1.: %#", _data); // Empty
return _data;
}
How can I return the result of the url session now? As you can see on my comments, the _data array is only filled on NSLog 3 and it is empty on 1 and 2. Would be nice if someone could tell me what I must change.

Related

ios - update UI inside block

I make a call to the youtube API to get the title of a video. I then want to display the title of the video on the screen in a table. How do I access the title after the block has finished executing?
Here's the code to get the title
-(void)getVideoTitle:(NSString *)urlStr success:(void (^)(NSDictionary *responseDict))success{
urlStr = [NSString stringWithFormat:#"https://www.googleapis.com/youtube/v3/videos?part=contentDetails%%2C+snippet%%2C+statistics&id=%#&key={API_KEY}",urlStr];
NSURL *url = [[NSURL alloc] initWithString:urlStr];
// Create your request
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// Send the request asynchronously
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
// Callback, parse the data and check for errors
if (data && !connectionError) {
NSError *jsonError;
NSDictionary *jsonResult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
if (!jsonError) {
success(jsonResult);
// NSLog(#"Response from YouTube: %#", jsonResult);
}
}
}] resume];
}
Here's how I call the above function:
[self getVideoTitle:#"zB4I68XVPzQ" success:^(NSDictionary *responseDict){
NSArray *itemsArray = [responseDict valueForKey:#"items"];
NSDictionary *item = itemsArray[0];
NSDictionary* snippet = [item valueForKey:#"snippet"];
NSString *title = [snippet valueForKey:#"title"];
}];
How do I get access the title variable outside the block after the block has finished executing?
I have tried the following with no luck
dispatch_async(dispatch_get_main_queue(), ^{
[self updateMyUserInterfaceOrSomething];
});
In your code:
NSString* recievedTitle __block = nil; //title is here, after block below run
[self getVideoTitle:#"zB4I68XVPzQ" success:^(NSDictionary *responseDict){
NSArray *itemsArray = [responseDict valueForKey:#"items"];
NSDictionary *item = itemsArray[0];
NSDictionary* snippet = [item valueForKey:#"snippet"];
recievedTitle = [snippet valueForKey:#"title"]; //here you write it
// or
NSString *title = [snippet valueForKey:#"title"];
[self updateInterfaceWithTitle: title]
}];
///
- (void)updateInterfaceWithTitle:(NSString*)title{
//use title here
}

Tableview data pass through Model Class

I have implemented a UITableview in VC1, and I want to display some JSON data in the cell. I have implemented a Model class to pass the data to the table view.
dispatch_async(dispatch_get_main_queue(), ^{
NSURLSession*session=[NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"https://itunes.apple.com/search?term=music"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", json);
NSArray *entryarr = [json objectForKey:#"results"];
TableClass *tc = [[TableClass alloc] init];
for (NSDictionary *appDict in entryarr) {
//setting title
NSString *str = [appDict objectForKey:#"artistName"];
[tc setTittle:str];
NSLog(#"artist Name=%#",tc.tittle);
//setting Subtitle
NSString *sub = [appDict objectForKey:#"country"];
[tc setSubtittle:sub];
NSLog(#"artist Name=%#",tc.subtittle);
//image
NSString *imageStr = [appDict objectForKey:#"artworkUrl60"];
NSURL *imageURL = [NSURL URLWithString:imageStr];
[tc setImage:imageStr];
NSData *imageData =[[NSData alloc] initWithContentsOfURL:imageURL];
//[self.imageArray addObject:imageData];
[_tableArray addObject:tc];
NSLog(#"%# name of tittle",[_tableArray objectAtIndex:0]);
}
NSLog(#"%lu %lu %lu",(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count);
[self.tableView reloadData];
}];
[dataTask resume];
});
But, while accessing it to the cell I am getting the last element in the array.
TableClass *tableclassModel = [self.tableArray objectAtIndex:indexPath.row];
cell.textLabel.text = tableclassModel.tittle;
cell.detailTextLabel.text = tableclassModel.subtittle;
cell.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:tableclassModel.image]]];
Why is it so...? How can I do it?
You are changing the value of same tc object again and again. Since you have declared tc object outside for loop, there will be only one tc. You are adding it to _tableArray after making the required changes in the first iteration of the loop. In the second iteration, you are changing the value of same tc object used in first iteration and adding it to _tableArray again. This will update the first object also with the new values. This goes on and finally your _tableArray will contain n number of tc objects with the same values (i.e last updated value)
Give the declaration inside the for loop
dispatch_async(dispatch_get_main_queue(), ^{
NSURLSession*session=[NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"https://itunes.apple.com/search?term=music"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", json);
NSArray*entryarr=[json objectForKey:#"results"];
for (NSDictionary*appDict in entryarr) {
TableClass*tc=[[TableClass alloc]init];
//setting tittl
NSString*str=[appDict objectForKey:#"artistName"];
[tc setTittle:str];
NSLog(#"artist Name=%#",tc.tittle);
//setting Subtittle
NSString*sub=[appDict objectForKey:#"country"];
[tc setSubtittle:sub];
NSLog(#"artist Name=%#",tc.subtittle);
//image
NSString*imageStr=[appDict objectForKey:#"artworkUrl60"];
NSURL*imageURL=[NSURL URLWithString:imageStr];
[tc setImage:imageStr];
NSData*imageData=[[NSData alloc]initWithContentsOfURL:imageURL];
//[self.imageArray addObject:imageData];
[_tableArray addObject:tc];
NSLog(#"%# name of tittle",[_tableArray objectAtIndex:0]);
}
NSLog(#"%lu %lu %lu",(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count,(unsigned long)self.tableArray.count);
[self.tableView reloadData];
}];
[dataTask resume];
});

Google Place api URL iOS

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];
}
}

Display Direction using google api in ios get

In Below code run so i get a response from url but when i try to get encodedPoints it give me a null value. also i update RegexKitLite but prob. not solve. Any suggestion are welcome Thank you advance.
NSString* saddr = [NSString stringWithFormat:#"%f,%f", f.latitude, f.longitude];
NSString* daddr = [NSString stringWithFormat:#"%f,%f", t.latitude, t.longitude];
NSString* apiUrlStr = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%#&destination=%#&sensor=false", saddr, daddr];
// http://maps.googleapis.com/maps/api/directions/json?origin=41.029598,28.972985&destination=41.033586,28.984546&sensor=false%EF%BB%BF%EF%BB%BF
NSURL *apiUrl = [NSURL URLWithString:[apiUrlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"api url: %#", apiUrl);
NSString *apiResponse = [NSString stringWithContentsOfURL:apiUrl encoding:nil error:nil];
NSString* encodedPoints = [apiResponse stringByMatching:#"points:\\\"([^\\\"]*)\\\"" capture:1L];
NSLog(#"encodedPoints: %#", encodedPoints);
if (encodedPoints) {
return [self decodePolyLine:[encodedPoints mutableCopy]];
}
else {
return NO;
}
I think its not a good way to do API request synchronously, especially when user' phone has poor internet connection, it will slow down the responsiveness of your application. So you should do an asynchronous API request with NSURLSession.
Also, the Directions API might return more than one routes for your request. So its better to use a NSArray to store your polyline points.
Sample code:
- (void)getPolyline {
NSURL *url = [[NSURL alloc] initWithString:#"https://maps.googleapis.com/maps/api/directions/json?origin=Chicago,IL&destination=Los+Angeles,CA&key=YOUR_API_KEY"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSError *jsonError;
NSDictionary *dict = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:nil error:&jsonError];
if (!jsonError) {
NSArray *routesArray = (NSArray*)dict[#"routes"];
NSMutableArray *points = [NSMutableArray array];
for (NSDictionary *route in routesArray) {
NSDictionary *overviewPolyline = route[#"overview_polyline"];
[points addObject:overviewPolyline[#"points"]];
}
NSLog(#"%#", points);
}
} else {
//print error message
NSLog(#"%#", [error localizedDescription]);
}
}] resume];
}

Retrieving data from NSDictionary and putting it into an NSMutableArray

I am trying to get a value from an NSDictionary and put it into an NSMutableArray. Here's some of my code for example.
- (IBAction)pressedButton:(id)sender {
NSInteger numberOfResults = 3;
NSString *searchString = #"EMINEM";
NSString *encodedSearchString = [searchString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *finalSearchString = [NSString stringWithFormat:#"https://itunes.apple.com/search?term=%#&entity=song&limit=%li",encodedSearchString,(long)numberOfResults];
NSURL *searchURL = [NSURL URLWithString:finalSearchString];
dispatch_queue_t iTunesQueryQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(iTunesQueryQueue, ^{
NSError *error = nil;
NSData *data = [[NSData alloc] initWithContentsOfURL:searchURL options:NSDataReadingUncached error:&error];
if (data && !error) {
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSArray *array = [JSON objectForKey:#"results"];
NSMutableArray *arrayTracks;
for (NSDictionary *bpDictionary in array) {
[arrayTracks addObject:[bpDictionary objectForKey:#"trackName"]];
// _author = [bpDictionary objectForKey:#"trackName"];
dispatch_async(dispatch_get_main_queue(), ^{
self.label.text = [arrayTracks objectAtIndex:0];
// self.label2.text = _author;
// NSLog(#"%#", [array objectAtIndex:0]);
});
}
}
});
}
As you can see from the above, it does not properly put the value into the array. If I set the _author to [bpDictionary objectForKey:#"trackName"] then it does work, but it goes through it all and sets the label as the last in the array. The above code outputs nothing (null).
Leverage foundation to your advantage, there's no need to iterate the array manually.
To get an array of track names you can simply use valueForKey: or valueForKeyPath:
NSArray *trackNames = [JSON valueForKeyPath:#"results.trackName"];
That being said, I urge you to perform networking tasks with networking API. You should never be using NSData to retrieve JSON. Stick to NSURLSession, it's cleaner and designed for networking tasks.
[[[NSURLSession sharedSession] dataTaskWithURL:itunesSearchURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// now you have a properly downloaded JSON
//and you even have access to the URL response headers and any errors.
}]resume];
Edit
As an aside, you never initialize your array of trackNames so calling addObject on a nil value will have no effect.

Resources