Reload table after request was finished - objective-c

I have a table view that use result of fetching data with NSURLSession as a datasource. Here is my NSArray which is responsible about that table.
#property (strong, nonatomic) NSMutableArray *results;
And this is my delegate and datasource method
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_results count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// Configure the cell...
WordResult *word = (WordResult *)[_results objectAtIndex:indexPath.row];
cell.textLabel.text = word.defid;
return cell;
}
In my viewDidLoad, I fetched request from Mashape and try to map the result into my custom class WordResult
Here is my fetch method
#pragma mark - GET Request
- (void)fetchDataFromMashape:(NSURL *)URL {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:#"GET"];
[request setValue:API_KEY_MASHAPE forHTTPHeaderField:API_MASHAPE_HEADER_1];
[request setValue:API_ACCEPT forHTTPHeaderField:API_MASHAPE_HEADER_2];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jsonResult = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
self.results = [self processResultData:jsonResult];
}];
[task resume];
}
- (NSMutableArray *)processResultData:(NSDictionary *)dict {
NSArray *list = [dict objectForKey:#"list"];
NSMutableArray *tempListOfWord = [[NSMutableArray alloc] init];
if (list) {
for (NSDictionary *item in list) {
WordResult *word = [[WordResult alloc] initWithDictionary:item];
[tempListOfWord addObject:word];
}
}
NSLog(#"Result array of word: %#", tempListOfWord);
return tempListOfWord;
}
My problem is, I dont know where to reload data after my result array was assigned by fetch method and dismissing my progress HUD that I showed on my viewDidLoad.
Here is my viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
[SVProgressHUD show];
[self fetchDataFromMashape:_finalURLrequest];
}
So where should I put [SVProgressHUD dismiss] and [self.tableView reloadData] after my request has been finished?
Any help would be appreciated.

Reload your table on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[_tableView reloadData];
});

A great tableView coding pattern is putting reload call in setters of model objects, then you won't miss any data change. For example setter for result in your case:
-(void)setResult:(NSMutableArray*)result{
_result = result;
[self.tableView reloadData]
[SVProgressHUD dismiss]
}

Try this:
You can put [SVProgress dismmis] and also reload your tableview [self.tableView reloadData] after self.results = [self processResultData:jsonResult];

In your viewdidload before calling fetchdata do
self.results=[[NSMutableArray alloc]init];
And then call your [self.tableView reloadData] within the completion block after assigning the array with data
And then call svprogress hud hide method outside the block after [task resume] in the fetch data function

Related

Populating Custom TableViewCell from Twitter using JSON Serialization

I am new to Objective-C. I spent countless hours being stuck on getting a blank tableview, I am desperate at this point.
I am loading twitter data through a JSON call, using their API. I store everything in a NSDictionary, run a for loop to select only "text" values. I store the filtered dictionary in an object which I later use in the TableView initialization.
I created a subclass of UItableViewCell for my custom cell.
My dataSources and delegates seem to be well connected as well (that is what I think at least)
I am having a hard time finding my problem. If someone could help me out please.
#import "ViewController.h"
#import "myCell.h"
#import <TwitterKit/TwitterKit.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize myTableView;
NSMutableArray *tweetObject;
NSDictionary *dictionary;
NSString *name;
NSString *text;
- (void)viewDidLoad {
tweetObject = [[NSMutableArray alloc] init];
[super viewDidLoad];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
text = #"text";
[[Twitter sharedInstance] logInGuestWithCompletion:^(TWTRGuestSession *guestSession, NSError *error) {
if (guestSession) {
// make API calls that do not require user auth
NSString *statusesShowEndpoint = #"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=goofernator";
NSError *clientError;
NSURLRequest *request = [[[Twitter sharedInstance] APIClient]
URLRequestWithMethod:#"GET"
URL:statusesShowEndpoint
parameters:0
error:&clientError];
if (request) {
[[[Twitter sharedInstance] APIClient]
sendTwitterRequest:request
completion:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&jsonError];
for (NSDictionary *dataDict in json) {
NSString *text = [dataDict valueForKeyPath: #"text"];
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:text,#"bodytext",nil];
[tweetObject addObject:dictionary];
}
}
else {
NSLog(#"Error: %#", connectionError);
}
}];
}
else {
NSLog(#"Error: %#", clientError);
}
} else {
NSLog(#"error: %#", [error localizedDescription]);
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return tweetObject.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
myCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell=[[myCell alloc]initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *tmpDict = [self.tweetObject objectAtIndex:indexPath.row];
cell.txtLblOutput.text = [tmpDict objectForKey:text];
[tableView reloadData];
return cell;
}
#end
Here you can see the way my storyboard is put together and the references I am using
http://postimg.org/image/79w7pqmu3/
http://postimg.org/image/ixq9kabyz/
You should call [tableView reloadData]; in your request completion handler, after you've filled your array. Check if you receive any data. Does the array get filled with dictionary objects?
But mate, seriously, you need to read some good books about coding, your code really lacks understanding of what you're doing. Please, remove [tableView reloadData]; from the - ...cellForRowAtIndexPath:... method.

variable becomes nil in numberOfRowsInTableView after being set in another method

My class looks like this :
#interface ApplicantPickerController : AppPage <NSTableViewDataSource, NSTableViewDelegate>
{
School *school;
__weak IBOutlet NSTableView *tableView;
NSMutableArray *familyList;
__weak IBOutlet NSProgressIndicator *progressIndicator;
}
- (IBAction)alphabetButtonPressed:(id)sender;
#end
In the alphabetButtonPressed method, I'm fetching a json array from a webservice and assigning it to familyList. After doing this, I do [tableView reload];
When the control passes to the - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView method, familyList becomes nil. Why is this happening and how can I fix it?
I'm using ARC for this project.
Cocoa/Objective-C newbie here. Any help would be much appreciated. Thank you!
Updated - Here is the implementation of the class :
#interface ApplicantPickerController ()
#end
#implementation ApplicantPickerController
- (IBAction)alphabetButtonPressed:(id)sender {
[progressIndicator startAnimation:self];
NSString * addy = [[NSString alloc] initWithFormat:#"%#.php?function=applicant_lookup&schoolID=%#&alpha=%#&currentYear=%#&format=json", BASE_URL_SCHOOL, school->recordID, [sender title], school->CurrentYear];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:addy]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if(data) {
//NSString * resp = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *error = nil;
NSObject *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if([json isKindOfClass:[NSDictionary class]])
{
familyList = nil;
[tableView reloadData];
}
else if ([json isKindOfClass:[NSArray class]])
{
familyList = [[NSMutableArray alloc] init];
[familyList addObjectsFromArray:(NSArray*)json];
//[_familyList retain];
[tableView reloadData];
}
[progressIndicator stopAnimation:self];
}
}];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [familyList count];
}
- (id)initWithMainView:(NSView *)_theView AndMainController:(NSViewController *)_theViewController AndNibName:(NSString *)nibName AndArgs:(NSArray *)_args
{
self = [super initWithMainView:_theView AndMainController:_theViewController AndNibName:nibName AndArgs:_args];
school = [args objectAtIndex:0];
return self;
}
#end
I don't know if I discovered a bug by apple, but following is how I solved this issue.
I was setting the NSTableView delegate and datasource in the UI builder (by right clicking and making the connections with the mouse). For some reason, if I set the delegate and dataSource in the code (specifically in the awakeFromNib method), the issue gets resolved.
- (void)awakeFromNib {
tableView.delegate = self;
tableView.dataSource = self;
}

reloadData for TableView not loading cellForRowAtIndexPath or row count

I am trying (in vain) to reload tableView in MasterViewController from another View Controller SitesViewController. I use this code in the SitesViewController:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = [[self tableView].indexPathForSelectedRow row];
//NSArray *appcell = [sitesMenu objectForKey:#"Table"];
NSLog(#"AppCell %#", sitesMenu);
NSDictionary *entry = [sitesMenu objectAtIndex:row];
self.siteid = [entry objectForKey:#"SITEID"];
NSLog(#" sample SiteView %#", siteid);
NDSClassMasterViewController *detailControllerTwo = [[NDSClassMasterViewController alloc] init];
detailControllerTwo.globalid = siteid;
NSLog(#"message %#", detailControllerTwo.globalid);
[detailControllerTwo fetchTweets];
dispatch_async(dispatch_get_main_queue(), ^{
[detailControllerTwo.tableView reloadData];
NSLog(#"%#", detailControllerTwo);
});
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
and this code for the method I am calling:
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *siteurl = [[NSString alloc] initWithFormat:#"http://adhoc.nyxtek.co.za/spfjsonws/default2.aspx?siteid=%#", globalid];
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: siteurl]];
NSError* error;
menuItems = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSLog(#"%#", menuItems);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
I have even added the reload code into the SiteViewController didSelectRow method.
I have read that I should add a property for it and synthesize but I have tried that but not sure how to add a property for UITableView to reference to the existing one.
The fetchTweets code runs, but the TableView doesn't reload.
Any assistance would be appreciated.
EDIT
This is the TableView code where I load the items in the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//NSString *name = [[[menuItems objectForKey:#"Table"] objectAtIndex:0] objectForKey:#"MENUID"];
NSDictionary *tweet = [[menuItems objectForKey:#"Table"] objectAtIndex:indexPath.row];
//NSLog(#"%#", tweet);
NSString *text = [tweet objectForKey:#"MENUDESC"];
NSString *name = [tweet objectForKey:#"MENUDESC"];
NSLog(#"TEST 1%#", text);
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}
Instead of exposing the table view via a property, why not simply write a function within the view controller that contains the code that would reload the data?
E.G. instead of:
[detailControllerTwo.tableView reloadData];
Declare a method in your MasterViewController that looks like:
- (void) updateTable
{
// tableView is declared as an IBOutlet
[tableView reloadData];
}
and then you can call that with:
dispatch_async(dispatch_get_main_queue(), ^{
[detailControllerTwo updateTable];
NSLog(#"%#", detailControllerTwo);
});

Storyboards UITableview with Custom Cell - Empty Array Error

I keep getting an error saying my table array is empty. Here's my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// Set navigation bar image
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"header.png"]
forBarMetrics:UIBarMetricsDefault];
spinnerActivity = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
spinnerActivity.hidesWhenStopped = YES;
spinnerBarButton = [[UIBarButtonItem alloc] initWithCustomView:spinnerActivity];
self.navigationItem.rightBarButtonItem = spinnerBarButton;
[spinnerActivity startAnimating];
self.testString = [[NSString alloc] init];
self.testMutableArray = [[NSMutableArray alloc] init];
self.myTableView = [[UITableView alloc] init];
[self.view addSubview:self.myTableView];
[self getMoviesInformation];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidUnload
{
[self setMenuBarButtonItem:nil];
[super viewDidUnload];
}
#pragma mark - Custom Methods
- (void)getMoviesInformation
{
NSURL *url = [NSURL URLWithString:#"http://api.rottentomatoes.com/api/public/v1.0/movies.json?apikey=6844abgw34rfjukyyvzbzggz&q=The+Man+With+The+Iron+Fists&page_limit=1"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
self.testMutableArray = [JSON objectForKey:#"movies"];
NSLog(#"Return String: %#", self.testMutableArray);
[self.myTableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
}
#pragma mark - Tableview Methods
#pragma mark DataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"movieTableCell";
MovieTableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[MovieTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
NSDictionary *dict = [self.testMutableArray objectAtIndex:indexPath.row];
NSLog(#"Dict: %#", dict);
cell.titleLabel.text = #"test";
NSLog(#"Cell TitleLabel: %#", cell.titleLabel.text);
return cell;
}
#pragma mark Delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 150;
}
Here's the error message I'm encountering:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for
empty array
Any idea why my dictionary is always nil and what I should adjust in my code to make it work? I basically patterned this to the example here:
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_afnetworking/
Change this line
self.testMutableArray = [JSON objectForKey:#"movies"];
to something like:
[self.testMutableArray addObject:(id)[JSON objectForKey:#"movies"]];
Your not adding in your objects to the array, your basically clearing it every time and assigning some object, that may or may not valid, to it. You need to consecutively add objects into the array if you want cellForRowAtIndexPath to work correctly.
Oh and the problem is not that your dictionary is nil. It's that your trying to grab a part of your array that doesn't exist/ hasn't had something assigned to it and assign that to your dictionary. Check your array count with testMutableArray.count to see how many objects you actually have in it.

Error when swipe to delete a table cell

I'm trying to delete a row when user swipes. I got this error driving me crazy. I've spent last three hours trying to figure out why. But, I have got no clue so far.
Here is my code to accomplish that.
in .h
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#interface FollowersTableViewController : UITableViewController
#property (nonatomic,strong)NSMutableArray *arrayWithUser ;
#end
and in .m i have this code .
#import "FollowersTableViewController.h"
#implementation FollowersTableViewController
#synthesize arrayWithUser ;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(void)viewDidLoad
{
[super viewDidLoad];
NSDictionary *dicUrlList= [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Urls" ofType:#"plist"]];
NSString *baseURl = [dicUrlList objectForKey:#"urlWithUser"];
baseURl = [baseURl stringByAppendingFormat:#"getfollowers"];
NSURL *urlToGetFollowers = [NSURL URLWithString:baseURl];
NSURLRequest *request = [NSURLRequest requestWithURL:urlToGetFollowers];
NSError *error = nil ;
NSURLResponse *response = nil ;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayWithUser count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"Cell";
CustomCell *customCell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (customCell == nil)
{
customCell = [[CustomCell alloc] initWithFrame:CGRectMake(0, 0, 320, 50)] ;
}
NSDictionary *dicWithUser = [arrayWithUser objectAtIndex:indexPath.row];
NSString *photoUrl = [dicWithUser objectForKey:#"profilePhotoUrl"];
if(![photoUrl isEqualToString:#""])
[customCell.thumbnail setImageWithURL:[dicWithUser objectForKey:#"profilePhotoUrl"] placeholderImage:[UIImage imageNamed:#"placeholder.png"] ];
else
{
[customCell.thumbnail setImage:[UIImage imageNamed:#"placeholder.png"]];
}
customCell.titleLabel.text = [dicWithUser objectForKey:#"username"];
UIButton *buttonFollow = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonFollow setTitle:#"Follow" forState:UIControlStateNormal];
CGRect frame = buttonFollow.frame ;
frame = CGRectMake(200, 10, 60, 30);
buttonFollow.frame = frame ;
buttonFollow.tag = indexPath.row ;
[buttonFollow addTarget:self action:#selector(followButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
customCell.accessoryView = buttonFollow ;
return customCell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 60 ;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[arrayWithUser removeObjectAtIndex:indexPath.row];
}
}
So far,I'm able to see delete button, but when i pressed it gives me this error
[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object.
Since I've already used NSMutableArray, I've no idea, why I'm getting this error?
I've already try to clean project. It didn't make any difference.
Your Json call returning an NSArray. You can go to create a mutableCopy - so you're will be able to use the "removeAtIndex.." method.
NSArray *rData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
arrayWithUser = [rData mutableCopy];
The assignment to arrayWithUser from the JSON call is returning an NSArray not an NSMutableArray in viewDidLoad. Fix that.
Actually, your array(arrayWithUser) is strongly pointing to the array return by the JSONObjectWithData, as you don't have the ownership of the returned array, you can't remove its object.
You better do one thing ,take ownership of that array.
arrayWithUser = [[NSMutableArray alloc]arrayByAddingObjectsFromArray:[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]];
Just replace below line
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
with
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
From Apple's documentation:
NSJSONReadingMutableContainers: Specifies that arrays and dictionaries are created as mutable objects.
NSJSONReadingMutableLeaves: Specifies that leaf strings in the JSON object graph are created as instances of NSMutableString.
You used in viwDidLoad
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
Lets try like this
NSMutableArray *userMutarr = [NSMutableArray alloc]initWithCapacity:3];
self.arrayWithUser = userMutarr;
[userMutarr release];
Then
self.arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];