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.