I started working with iOS. And using Storyboard to navigate between the view controllers. First screen is an uiviewcontroller(login screen) and second one is an uiviewcontroller with a table view. I'm sending some web requests in the first viewcontroller and then performing segue to the next viewcontroller. There is about 9 seconds delay to load the second view controller after getting the response. I tried checking the cause using Timer profiler and didnt find anything in my obj-c code.
If anyone can help me here is the link to my trace file.
My Log:
2014-05-08 10:12:12.601 Consumer[6713:4207] One
2014-05-08 10:12:12.602 Consumer[6713:4207] Three
2014-05-08 10:12:12.605 Consumer[6713:4207] Four
2014-05-08 10:12:12.606 Consumer[6713:4207] Two
2014-05-08 10:12:21.394 Consumer[6713:60b] numberOfSectionsInTableView
2014-05-08 10:12:21.395 Consumer[6713:60b] titleForHeaderInSection
2014-05-08 10:12:21.395 Consumer[6713:60b] numberOfRowsInSection
2014-05-08 10:12:21.395 Consumer[6713:60b] titleForHeaderInSection
2014-05-08 10:12:21.395 Consumer[6713:60b] numberOfRowsInSection
2014-05-08 10:12:21.396 Consumer[6713:60b] titleForHeaderInSection
2014-05-08 10:12:21.396 Consumer[6713:60b] numberOfRowsInSection
2014-05-08 10:12:21.396 Consumer[6713:60b] cellForRowAtIndexPath
2014-05-08 10:12:21.399 Consumer[6713:60b] returning cell
2014-05-08 10:12:21.399 Consumer[6713:60b] cellForRowAtIndexPath
2014-05-08 10:12:21.400 Consumer[6713:60b] returning cell
2014-05-08 10:12:21.401 Consumer[6713:60b] cellForRowAtIndexPath
2014-05-08 10:12:21.402 Consumer[6713:60b] returning cell
2014-05-08 10:12:21.402 Consumer[6713:60b] titleForHeaderInSection
My LoginViewController:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"login_success_new"]){
TableViewController *controller = (TableViewController *)segue.destinationViewController;
controller.municipalitiesArray = municipalitiesArray;
controller.muni_metergroup_dict = muni_metergroup_dict;
NSLog(#"Three");
}
}
-(void) sendPostRequest:(NSURL *)requestURL requestParams:(NSDictionary *)params requestType:(NSInteger)type{
NSError *error;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
//[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setHTTPMethod:#"POST"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
[request setHTTPBody:postData];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"response = %#",json);
if(json != nil){
switch (type) {
case 1:
{
for (NSObject *object in [json objectForKey:#"municipalities"]){
MunicipalityModel *muniModel = [[MunicipalityModel alloc] init];
[muniModel setModelValues:object];
[municipalitiesArray addObject:muniModel];
}
//municipalitiesArray = [json objectForKey:#"municipalities"];
//MunicipalitiesModel *muniModel = [[MunicipalitiesModel alloc] init];
//[muniModel setModelValues:municipalitiesArray[0]];
userEmail = #"admin#example.com";
tokenValue = [json objectForKey:#"token"];
NSString *stringUrl = [NSString stringWithFormat:#"http://%#.quality.sentry-link.com/api/v1/meter_groups.json",[municipalitiesArray[count] muniSubdomain]];
NSURL *url = [NSURL URLWithString:stringUrl];
[self sendGetRequest:url requestParams:nil requestType:2];
break;
}
default:
break;
}
//[self performSegueWithIdentifier:#"login_success" sender:self];
}
}];
[postDataTask resume];
}
-(void) sendGetRequest:(NSURL *)requestURL requestParams:(NSDictionary *)params requestType:(NSInteger)type{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSLog(#"Request URL =====> %#",requestURL);
NSURLSessionDataTask *getDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"response = %#",json);
if(json != nil){
switch (type) {
case 1:
{
break;
}
case 2:
{
NSMutableArray *meterGroupsArray = meterGroupsArray = [[NSMutableArray alloc] init];
for (NSObject *object in [json objectForKey:#"meter_groups"]){
MeterGroupModel *meterGroupModel = [[MeterGroupModel alloc] init];
[meterGroupModel setModelValues:object];
[meterGroupsArray addObject:meterGroupModel];
}
count++;
[muni_metergroup_dict setValue:meterGroupsArray forKey:[municipalitiesArray[count-1] muniSubdomain]];
if (count < [municipalitiesArray count]) {
NSString *stringUrl = [NSString stringWithFormat:#"http://%#.quality.sentry-link.com/api/v1/meter_groups.json",[municipalitiesArray[count] muniSubdomain]];
NSURL *url = [NSURL URLWithString:stringUrl];
[self sendGetRequest:url requestParams:nil requestType:2];
}
break;
}
default:
break;
}
if(count ==[municipalitiesArray count]){
NSLog(#"One");
[self performSegueWithIdentifier:#"login_success_new" sender:self];
NSLog(#"Two");
}
}
}];
[getDataTask resume];
}
TableviewController:
- (void)viewDidLoad
{
[super viewDidLoad];
//tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
NSLog(#"Four");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)buttonClicked:(id)sender{
NSLog(#"button Clicked");
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
NSLog(#"numberOfSectionsInTableView");
return [_municipalitiesArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//Number of rows it should expect should be based on the section
NSString *sectionSubdomain = [_municipalitiesArray[section] muniSubdomain];
NSArray *array = [_muni_metergroup_dict objectForKey:sectionSubdomain];
NSLog(#"numberOfRowsInSection");
return [array count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSLog(#"titleForHeaderInSection");
return [self.municipalitiesArray [section] muniName];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cellForRowAtIndexPath");
long row = [indexPath row];
long section = [indexPath section];
NSArray *meterGroupArray = [_muni_metergroup_dict valueForKey:[_municipalitiesArray[section] muniSubdomain]];
static NSString *CellIdentifier = #"tableCell";
TableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
// Configure the cell...
[cell.lblMeterGroupName setText:[meterGroupArray[row] groupName]];
[cell.lblMeterGroupId setText:[meterGroupArray[row] groupId]];
[cell.lblLatitude setText:[meterGroupArray[row] latitude]];
[cell.lblLongitude setText:[meterGroupArray[row] longitude]];
NSLog(#"returning cell");
return cell;
}
Thanks in advance!
The problem, clearly displayed in your log, is that One, Two, Three, and Four are being performed on a background thread. This means, for example, that you are saying:
[self performSegueWithIdentifier:#"login_success_new" sender:self];
...on a background thread. That is so wrong. You must NEVER say anything to or about the interface on a background thread. You need to get your threading straightened out. (The fact that One, Two, Three, and Four do not happen in order is also a clue that you may have set this up incoherently.)
EDIT: In a comment, you say that you now used dispatch_async(dispatch_get_main_queue()..., thus correctly moving the call to performSegueWithIdentifier: onto the main thread. Excellent! Your logging should now show that all calls connected with the interface (including especially "Three" in prepareForSegue:) are now on the main thread.
However, you still need to be careful. Basically everything in both completion: handlers happens on a background thread! You are even calling [self sendGetRequest:...] on a background thread! And you are apparently talking to instance variables of self on this a background thread. All of that is dangerous. What I would do is step out to the main thread right at the start of each completion handler, so that even if the completion handler is called on a background thread, everything I then do happens on the main thread.
Make sure that you are not performing your web request on the main thread which will block your UI and wait for the request to finish before performing the segue.
Related
I have a class for get xml and parse it.
+ (instancetype)sharedRssNewsLoader
{
static BGMRssNewsLoader *sharedRssNewsLoader = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedRssNewsLoader = [[self alloc] initPrivate];
});
return sharedRssNewsLoader;
}
- (instancetype)initPrivate
{
self = [super init];
//did the superclass's designated initializer succeed?
if (self)
{
self.rssNewsItems = [[NSMutableArray alloc] init];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:nil];
//RSS call
[self fetchFeed];
}
return self;
}
- (void)fetchFeed
{
NSString *requestString = #"http://xxx...";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
self.rssXmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
DDLogVerbose(#"rssXmlString: %#", self.rssXmlString);
dispatch_sync(dispatch_get_main_queue(), ^{
[self parseXML:self.rssXmlString];
});}
];
[dataTask resume];
}
//parse XML
- (void)parseXML:(NSString *)source
{
NSError *error = nil;
DDXMLDocument *theDocument = [[DDXMLDocument alloc] initWithXMLString:source
options:0
error:&error];
NSArray *xmlItems = [theDocument nodesForXPath:#"//item"
error:&error];
for(DDXMLElement *itemElement in xmlItems)
{
NSString *itemTitleValue = [[[itemElement elementsForName:#"title"] lastObject] stringValue];
NSString *itemDescriptionValue = [[[itemElement elementsForName:#"title"] lastObject] stringValue];
BGMRssNewsEntry *rssEntry = [[BGMRssNewsEntry alloc] initRssNewsEntryWithTitle:itemTitleValue
rssText:itemDescriptionValue
rssUrl:nil
rssDate:nil
isRssRead:NO];
[self.rssNewsItems addObject:rssEntry];
}
}
//if a programmer calls [[BGMItemsStore alloc] init], let him know the error of his ways
- (instancetype) init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"You are wrong! Use +[BBGMRssNewsLoader sharedRssNewsLoader]"
userInfo:nil];
return nil;
}
I would like to show result in the table. So in the other class I do:
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self)
{
//custom initialization
//initialization sharedRssNewsLoader with rssItems array and RSS call
[BGMRssNewsLoader sharedRssNewsLoader];
}
return self;
}
But my table is absolutely clear. I know that I need to switch to main_queue when I am working with NSURLSessionDataTask, I did it. I think that I mess with thread :(
That BGMRssNewsLoader is asynchronously loading the data and in parseXML you are adding entries to the self.rssNewsItems object. But I don't see where it tells the table to reload itself when this asynchronous request done. If your table view's data source tries to immediately retrieve the rssNewsItems array, it will likely be empty until the asynchronous request is complete.
Generally, when you're done loading the data, you'd call [self.tableView reloadData] (either by supplying the tableview as a parameter during the instantiation of the BGMRssNewsLoader, or by having it post a notification that the table view controller is observing, or by supplying a completion block parameter in which you'd perform reloadData).
I'd also suggest temporarily adding a breakpoint or log statement where you add objects to the rssNewsItems array, just to make sure that that the XML feed was successfully retrieved and parsed.
For example, I might retire fetch and define a method like so:
- (void)fetchFeedWithCompletionBlock:(void (^)(NSArray *items, NSError *error))completion
{
NSString *requestString = #"http://xxx...";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
self.rssXmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
DDLogVerbose(#"rssXmlString: %#", self.rssXmlString);
dispatch_sync(dispatch_get_main_queue(), ^{
[self parseXML:self.rssXmlString];
if (completion) {
completion(self.rssNewsItems, nil);
}
});
}];
[dataTask resume];
}
I would, by the way, not have the initPrivate method initiate the fetch. You want the caller to be able to supply whatever completion block it wants.
I'd then have the tableview's controller do something like:
[[BGMRssNewsLoader sharedRssNewsLoader] fetchFeedWithCompletionBlock:^(NSArray *items, NSError *error) {
// use the items array and/or error
[self.tableView reloadData];
}];
Clearly, the fetchFeedWithCompletionBlock should actually detect errors and call the completion block with the appropriate NSError (so the table view can decide how it wants to present the error ... the singleton shouldn't be doing anything with the UI itself), but hopefully this illustrates the idea of a network object that provides a completion block to allow the caller to specify what it wants to do when the asynchronous request is complete.
hi i tried to display images from json to CollectionView .
my code is...
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://zoo.com/jh/image.php"]];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json;charset=UTF-8" forHTTPHeaderField:#"content-type"];
NSError *err;
NSURLResponse *response;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData:responseData options: NSJSONReadingMutableContainers error: &err];
NSArray *headlines=[jsonArray1 objectForKey:#"image_gallery"];
for (int i=0;i<[headlines count]; i++)
{
NSString *moblinks=[[headlines objectAtIndex:i]objectForKey:#"image_url"];
[self.itemshowdetailsAr objectAtIndex:moblinks];
NSLog(#"%#",moblinks);
}
and my result in console
2014-06-20 15:05:27.005 Collection[3945:60b] http://xxxxxx/jh/ju_image/gal_1.jpg
2014-06-20 15:05:27.007 Collection[3945:60b] http://xxxxxx/jh/ju_image/gal_2.jpg
2014-06-20 15:05:27.007 Collection[3945:60b] http://xxxxxx/jh/ju_image/gal_3.jpg
2014-06-20 15:05:27.007 Collection[3945:60b] http://xxxxxx/jh/ju_image/gal_4.jpg
2014-06-20 15:05:27.008 Collection[3945:60b] http://xxxxxx/jh/ju_image/gal_5.jpg
2014-06-20 15:05:27.008 Collection[3945:60b] http://xxxxxx/jh/ju_image/gal_6.jpg
my collectionview code
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
NSString *ImageURL =[self.itemshowdetailsAr objectAtIndex:indexPath.row];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:ImageURL]];
cell.imageView.image = [UIImage imageWithData:imageData];
cell.imageView.userInteractionEnabled=YES;
cell.imageView.contentMode = UIViewContentModeScaleToFill;
// cell.imageView.tag=i-1;
return cell;
}
but i got empty Collection View only.where i made mistake?i got array of url in console but cell is not displaying images.
By the looks of it you're trying to download image on the main thread and I'm guessing that the UI might be freezing while network requests are processed. Instead load the images asynchronously and away from the main thread by doing something like
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
NSURLSession *session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[self.itemshowdetailsAr objectAtIndex:indexPath.row]]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = [UIImage imageWithData:data];
cell.imageView.userInteractionEnabled=YES;
cell.imageView.contentMode = UIViewContentModeScaleToFill;
}];
}] resume];
return cell;
}
The NSURLSession task will run away from the main thread. The completion handler will be called when the imageData has been downloaded. At this point an operation is added to the main thread to update the imageView within your collectionViewCell.
I'm playing around with a tutorial for NSURLSession. I can successfully download an image, however the delegates for download progress and download finished are not triggering. Here is the code :
- (void)viewDidLoad
{
[super viewDidLoad];
NSString * imageUrl = #"http://ichef.bbci.co.uk/naturelibrary/images/ic/credit/640x395/r/ro/rock_pigeon/rock_pigeon_1.jpg";
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
//Download image.
NSURLSessionDownloadTask * getImageTask = [session downloadTaskWithURL:[NSURL URLWithString:imageUrl]
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"Error sadly for you is %#", [error localizedDescription]);
}
UIImage * downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
dispatch_async(dispatch_get_main_queue(), ^ {
self.imageView.image = downloadedImage;
});
}];
[getImageTask resume];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(#"Temporary File :%#\n", location);
NSError *err = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSURL *docsDirURL = [NSURL fileURLWithPath:[docsDir stringByAppendingPathComponent:#"out1.zip"]];
if ([fileManager moveItemAtURL:location
toURL:docsDirURL
error: &err])
{
NSLog(#"File is saved to =%#",docsDir);
}
else
{
NSLog(#"failed to move: %#",[err userInfo]);
}
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
//You can get progress here
NSLog(#"Received: %lld bytes (Downloaded: %lld bytes) Expected: %lld bytes.\n",
bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
And in the .h file :
#import <UIKit/UIKit.h>
#interface SGGViewController : UIViewController <NSURLSessionDelegate> {
IBOutlet UIImageView * imageView;
}
#property (nonatomic, strong) IBOutlet UIImageView * imageView;
#end
Can anyone suggest how to fix ?
Use NSUrlRequest Now delegates will call . Hope this will work
- (void)viewDidLoad
{
[super viewDidLoad];
NSURLSessionDownloadTask *downloadTask =nil;
NSString * imageUrl = #"http://fc05.deviantart.net/fs71/i/2012/180/8/f/ios_6_logo_psd___png_by_theintenseplayer-d55eje9.png";
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]] ;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]];
downloadTask = [session downloadTaskWithRequest:request];
[downloadTask resume ];
/*
NSURLSessionDownloadTask * getImageTask = [session downloadTaskWithURL:[NSURL URLWithString:imageUrl]
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"Error sadly for you is %#", [error localizedDescription]);
}
UIImage * downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
dispatch_async(dispatch_get_main_queue(), ^ {
//self.imageView.image = downloadedImage;
});
}];
[getImageTask resume];
*/
// Do any additional setup after loading the view, typically from a nib.
}
You already have a delegate, so you may as well skip the the completionHandler/block form of task creation, and go all-in on the delegate.
A quick glance at the holy script didn't tell me anything authoritative about whether specifying a completion handler will prevent the delegate methods from being fired, but there is a lot about that relationship that seems mutually exclusive.
If you haven't already, I'd say you should add –URLSession:task:didCompleteWithError: to your delegate. It might capture problems the purely download delegate methods might miss.
i'm trying to fill my UITableView with this JSON data using the ASIhttprequest, but ik get the following exception: __NSArrayM objectForKey:]: unrecognized selector sent to instance
I use the following code in my controller to achieve this:
#import <UIKit/UIKit.h>
#interface tableListViewController : UITableViewController {
NSDictionary *data;
}
#end
And the main file:
#import "tableListViewController.h"
#import "ASIHTTPRequest.h"
#import "ASIFormDataRequest.h"
#interface tableListViewController ()
#end
#implementation tableListViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"http://api.glennzo.nl/glow/index.json"];
ASIHTTPRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setTag:100];
[request setDelegate:self];
[request startSynchronous];
//Set the title
self.navigationItem.title = #"Events";
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (void) requestFinished:(ASIHTTPRequest *) request
{
if (request.tag == 100) {
NSData *responseData = [request responseData];
NSError *error;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
if(!JSON || ![JSON count])
{
NSLog(#"%#", #"geen correcte json data van request gekregen");
}
else
{
data = JSON;
}
}
}
- (void) requestFailed:(ASIHTTPRequest *) request
{
NSError *error = [request error];
NSLog(#"Error: %#",error.localizedDescription);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [UITableViewCell alloc];
}
NSArray *namen = [data objectForKey:#"name"];
cell.textLabel.text = [namen objectAtIndex:indexPath.row];
return cell;
}
Thanks to some debugging i now know that the ASIHttpRequest works, but the NSdictionary that i create (and intent to use around the entire app) gives me the trouble. Can somebody set me in the right direction?
The error seems to be at:
NSArray *namen = [data objectForKey:#"name"];
So I think data type isn't correct (NSDictionary), is nil or it is filled with another type:
[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
Try seeing what arrives to you via JSON. For example:
NSError *jsonError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];
if ([jsonObject isKindOfClass:[NSArray class]]) {
NSLog(#"its an array!");
NSArray *jsonArray = (NSArray *)jsonObject;
NSLog(#"jsonArray - %#",jsonArray);
} else {
NSLog(#"its probably a dictionary");
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
NSLog(#"jsonDictionary - %#",jsonDictionary);
}
following code uses ASIhttprequest to fill appData array. View also includes a tableview and when is launched, getData function is executed first but then I would like to wait until request ends and appData is filled before execute tableView init methods. Now tableView methods are executed before request ends. How to do that? Thank you.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
NSArray *datos = [[NSArray alloc] initWithObjects:(#"test"), nil];
self.appData = datos;
[datos release];
}
[self getData];
return self;
}
- (void)getData
{
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
NSURL *url = [NSURL URLWithString:#"http://test.com/iphone.php"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
NSLog(#"URL = %#",url);
[request setValidatesSecureCertificate:NO];
[request setRequestMethod:#"POST"];
[request setPostValue:#"admin" forKey:#"key1"];
[request setPostValue:#"admin" forKey:#"key1"];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"%#", [request responseString]);
//NSLog(#"Response %d ==> %#", request.responseStatusCode, [request responseString]);
[activityIndicator stopAnimating];
activityIndicator.hidden = YES;
appData = [[request responseString] componentsSeparatedByString: #"#"];
int x =0;
while (x<[appData count] - 1)
{
NSLog(#"cells = %#",[appData objectAtIndex: x]);
x = x+1;
}
}
You have two options here:
you alloc and init your tableview AFTER the request did finished - this is not possible if you used IB to create your tableview
you return 0 in your UITableViewDelegates - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method until the request is not finished. Then, reload the table and return the actual record count.