Objective C - Table View: load items from database - objective-c

I have a table view and until now I fill the table with spelled numbers like this:
- (NSMutableArray *)data {
if(!_data){
_data = [[NSMutableArray alloc] init];
// spelled numbers
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterSpellOutStyle;
for(int i = 0; i < 20; i++){
NSNumber *thisNumber = [NSNumber numberWithInt:i];
NSString *spelledOutNumber = [formatter stringFromNumber:thisNumber];
[_data addObject:spelledOutNumber];
}
}
return _data;
}
But how can I fill the table now with dynamic items from a database?
This is how I get the data from database:
NSURLSession *session = [NSURLSession sharedSession];
NSString *url = [NSString stringWithFormat:#"http://www.example.net/iOS/game/getData.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 *jsondata = [json objectForKey:#"data"];
for (int i = 0; i < [jsondata count]; i++) {
NSString *name = [[jsondata objectAtIndex:i] objectForKey:#"name"];
[_data addObject:name];
}
}
}];
[dataTask resume];
But how can I load this data now to the table view? If I just put the database code to the data method it doesnt work, because the database code is asynchron. Can someone help me with it?
Edit:
Here are the data methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.textLabel.text = [self.data objectAtIndex:indexPath.row];
return cell;
}

Related

Display records of next page in json in uitableview on scrolling the tableView when I reach the last record

I am trying to make an application that uses SBJSON and ASIHTTPRequest containing 273 pages (50 records in each page)in json. When I scroll the tableView I want to get next 50 records when I reach the last record.Currently I am getting only the first 50 records. How will I get all the records? I am using Xcode 6 objective c.
//Here I am taking the json data on view load
- (void)viewDidLoad
{
[super viewDidLoad];
main_array = [[NSMutableArray alloc] init];
NSURL *countryURL = [NSURL URLWithString:#"http://api.worldbank.org/countries/all/indicators/SP.POP.TOTL?format=json"];
ASIHTTPRequest *request = [ASIFormDataRequest requestWithURL:countryURL];
[request setDelegate:self];
[request startSynchronous];
[self setTitle:#"COUNTRIES"];
}
-(void) requestFinished: (ASIHTTPRequest *) request
{
NSString *theJSON = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSMutableArray *jsonDictionary = [parser objectWithString:theJSON error:nil];
//Here I am separating the different arrays and storing it in model class
NSDictionary *pagesDict = [jsonDictionary objectAtIndex:0];
NSMutableArray *dataArray = [jsonDictionary objectAtIndex:1];
for (NSDictionary *dictionary in dataArray)
{
Model *model = [[Model alloc]init];
NSDictionary *tmpdict =[dictionary objectForKey:#"indicator"];
NSDictionary *tmpcountdict =[dictionary objectForKey:#"country"];
model.indicator = [tmpdict objectForKey:#"value"];
model.country = [tmpcountdict objectForKey:#"value"];
model.date = [dictionary objectForKey:#"date"];
model.value = [dictionary objectForKey:#"value"];
model.decimal = [dictionary objectForKey:#"decimal"];
//main_array is the main mutable array to store values of model class
[main_array addObject:model];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [main_array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SimpleTableViewCell";
SimpleTableViewCell *cell = (SimpleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
Model *model = [[Model alloc]init];
model = [self.main_array objectAtIndex:indexPath.row];
//Display value in cells
cell.indicatorLabel.text = [NSString stringWithFormat:#"%#",model.indicator];
cell.countryLabel.text = [NSString stringWithFormat:#"%#",model.country];
cell.yearLabel.text = [NSString stringWithFormat:#"%#",model.date];
cell.valueLabel.text = [NSString stringWithFormat:#"%#",model.value];
cell.decimalLabel.text = [NSString stringWithFormat:#"%#",model.decimal];
return cell;
}
Have you tried using ‘page’ parameter in your query?
http://api.worldbank.org/countries/all/indicators/SP.POP.TOTL?format=json&page=2
If you want to list all records, you can use ‘per_page’ parameter, which I would not recommend
http://api.worldbank.org/countries/all/indicators/SP.POP.TOTL?format=json&per_page=13640
You can set some tag on your last cell and then send call to the server once you reach to that cell. Once you received data from server append it to your table view data and reload the data.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"mycell"];
if(indexPath.row == [data count])
{
cell.tag = SOME_TAG_OF_LAST_CELL;
}
return cell;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(cell.tag == SOME_TAG_OF_LAST_CELL)
{
// send call to server to fetch next page from server.
}
}

Make lazy loading faster and responsive in iOS App

In my iOS app I've a class that performs a web request to get an array of informations located in mySQL DB. In this class I've a method that do this taking as input a mySQL query:
- (NSMutableArray *) myreq:(NSString *)query{
// Create NSData object
NSData *dataQuery = [query
dataUsingEncoding:NSUTF8StringEncoding];
// Get NSString from NSData object in Base64
NSString *base64EncodedQuery = [dataQuery base64EncodedStringWithOptions:0];
// Print the Base64 encoded string
NSLog(#"Encoded: %#", base64EncodedQuery);
NSMutableString *strURL = [NSMutableString stringWithFormat:#"http://…=%#“,base64EncodedQuery];
[strURL setString:[strURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:dataURL
options:kNilOptions
error:&error];
NSMutableArray *results = [[NSMutableArray alloc] init];
int numRow = 0;
for (NSArray *arrow in json) {
[results addObjectsFromArray:arrow];
numRow++;
}
return results;
}
This method send a query to a php script that perform immediately this query to MySQL DB and get a json with results. I translate the json in this method and finally return an array with results.
I call myreq in a method
- (void)downloadScope{
_arrID = [[NSMutableArray alloc] init];
_arrIDUsers = [[NSMutableArray alloc] init];
_arrUsernames = [[NSMutableArray alloc] init];
_arrPictures = [[NSMutableArray alloc] init];
[myQueue addOperation:[NSBlockOperation blockOperationWithBlock: ^{
query = #"SELECT ID FROM mytable”;
[_arrID addObjectsFromArray:[self myreq:query]];
for (int i = 0; i < [_arrID count]; i++) {
NSArray *tempArray = [[NSArray alloc] initWithArray:[self myreq:[NSString stringWithFormat:#"SELECT IDUsr,usrn, pictureaddress FROM mytable WHERE ID = %#",_arrID[i]]]];
[_arrIDUsers insertObject:tempArray[0] atIndex:i];
[_arrUsernames insertObject:tempArray[2] atIndex:i];
[_arrPictures insertObject:tempArray[2] atIndex:i];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.tableView reloadData];
}];
}
}]];
[myQueue setSuspended:NO];
}
In tableView I create cells in this way (using SDWebImage):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Identificatore di cella
NSString *identifier = #“cellmy”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.backgroundColor = nil;
if ( cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
NSString *username = [self.arrUsernames objectAtIndex:indexPath.row];
UILabel *cellLabelUsername = (UILabel *)[cell viewWithTag:2];
cellLabelUsername.text = [username uppercaseString];
UIImageView *cellImageProfileSnap = (UIImageView *)[cell viewWithTag:5];
[cellImageProfileSnap sd_setImageWithURL:[NSURL URLWithString:[_arrPictures objectAtIndex:indexPath.row]] placeholderImage:[UIImage imageNamed:#“…”]];
}
In viewDidLoad I initialize my NSOperationQueue (defined in my interface):
myQueue = [[NSOperationQueue alloc] init];
[myQueue setMaxConcurrentOperationCount:100];
[myQueue setName:#"com.sada"];
My goal is to make everything faster because the loading in tableView is slow and I think that is not dependent on SDWebImage. Please help me

Using Images To Populate UITableView

I'm very new to iOS programming, and am trying to figure out something that is probably trivial. I'm trying to fetch Json data from Flickr, specifically just images, and use this data to populate pictures on a UITableView. This is my attempt:
- (void)jsonParsing
{
//-- Make URL request with server
NSHTTPURLResponse *response = nil;
NSString *jsonUrlString = [NSString stringWithFormat:#"http://api.flickr.com/services/feeds/photos_public.gne?tagmode=any&format=json&tags=QUERY"];
NSURL *url = [NSURL URLWithString:[jsonUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//-- Get request and response though URL
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
//-- JSON Parsing
self.flickrArray = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"Result = %#",self.flickrArray);
for (NSMutableDictionary *dic in self.flickrArray)
{
NSString *string = dic[#"media"];
if (string)
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
dic[#"media"] = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
}
else
{
NSLog(#"Error in url response");
}
}
}
- (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 [self.flickrArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSData *imageData = [self.flickrArray objectAtIndex:indexPath.row];
cell.image = [UIImage imageWithData:imageData];
return cell;
}
The only thing happening in the NSLog is : Result = (null). I can't for the life of me figure out what I'm doing incorrectly.

NSMutableArray partly empty after recalled

I've got a property NSMutableArray in my view controller
ContactsViewController.h:
#interface ContactsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic,strong) NSMutableArray *contacts;
...
#end
In this view controller I fill my array on "viewDidLoad"
ContactsViewController.m:
#implementation ContactsViewController
#synthesize contacts;
...
...
- (void)viewDidLoad
{
[super viewDidLoad];
DBhandler *handler = [[DBhandler alloc] init];
if (contacts)
[contacts removeAllObjects];
else
contacts = [[NSMutableArray alloc] init];
// Get all my contacts that are in my core data file
// This function returns a NSMutableArray
contacts=[handler getContacts];
//loop through contacts of addressbook when user wants that
if ([allContactSwitch isOn])
{
//open link to addressbook
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );
for( CFIndex personIndex = 0; personIndex < nPeople; personIndex++ ) {
ABRecordRef refVal = CFArrayGetValueAtIndex( allPeople, personIndex );
Boolean newContact = true;
// check if contact is already in Core data File
for( CFIndex i = 0; i < [contacts count]; i++ ) {
contact *checkcontact=[contacts objectAtIndex:i];
if (personIndex==checkcontact.personRef)
newContact = FALSE;
}
if (newContact)
{
contact *dummycontact = [[contact alloc]init];
dummycontact.personRef = personIndex;
dummycontact.contactName = (__bridge NSString *)(ABRecordCopyCompositeName( refVal ));
// Add contact to array
[contacts addObject:dummycontact];
}
}
}
// Just to check, the entire array looks fine!
for( CFIndex i = 0; i < [contacts count]; i++ ) {
contact *dummycontact=[contacts objectAtIndex:i];
NSLog(#"Name after build: %#", dummycontact.contactName);
}
}
But later when the different cell for the table view are filled, the part of the NSMutableArray that came from [handle getContacts] are empty:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//NSLog(#"cell number %i",indexPath.row);
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Set up the cell...
contact *dummycontact=[contacts objectAtIndex:indexPath.row];
// Only part of the objects in the array contacts contain data!
NSLog(#"Name cell: %# %i", dummycontact.contactName, indexPath.row);
cell.textLabel.text = dummycontact.contactName;
return cell;
}
This probably has to do with the fact that the memory of the objects created in [handle getContacts] is cleared in the meantime. But I don't know how to solve this. I've tried clone or copy the output of [handle get contacts], but I wasn't successful.
To be complete the function "getContacts":
-(NSMutableArray*)getContacts{
NSMutableArray *contacts = [[NSMutableArray alloc] init];
NSManagedObjectContext *context = [self managedObjectContext];
DBcontact *contact = [NSEntityDescription
insertNewObjectForEntityForName:#"WhappContacts"
inManagedObjectContext:context];
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"WhappContacts"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (contact in fetchedObjects) {
[contacts addObject:contact];
// Data seems fine here.
NSLog(#"Name in: %#", contact.contactName);
}
return contacts;
}
Any help would be very appreciated!
An alternative would be to place the contact information in a dictionary:
- (NSMutableArray*)getContacts {
NSMutableArray *contacts = [[NSMutableArray alloc] init];
NSManagedObjectContext *context = [self managedObjectContext];
DBcontact *contact = [NSEntityDescription insertNewObjectForEntityForName:#"WhappContacts" inManagedObjectContext:context];
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"WhappContacts" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (contact in fetchedObjects) {
NSDictionary *contactDict = [NSDictionary dictionaryWithObjectsAndKeys:
contact.contactName, #"contactName", nil]; //add other necessary contact information
[contacts addObject:contactDict];
// Data seems fine here.
NSLog(#"Name in: %#", contact.contactName);
}
return contacts;
}
And to retrieve the information:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//NSLog(#"cell number %i",indexPath.row);
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Set up the cell...
NSDictionary *dummycontact = [contacts objectAtIndex:indexPath.row];
// Only part of the objects in the array contacts contain data!
NSLog(#"Name cell: %# %i", [dummycontact objectForKey:#"contactName"], indexPath.row);
cell.textLabel.text = [dummycontact objectForKey:#"contactName"];
return cell;
}

Fill up table view with UISearchDisplayController leads to EXC_BAD_ACESS! WHY?

I made some test with the UISearchDisplayController and I found some strange behavior I can not explain properly.
Please take a look at the following code:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return numOfRecords;
}
- (NSArray*) search:(NSString*)query {
// Prepare URL request to download statuses from Twitter
NSString *urlString = [NSString stringWithFormat:#"http://someaddress/search.ac?term=%#", query];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
// Perform request and get JSON back as a NSData object
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// Get JSON as a NSString from NSData response
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSArray *parsedResult = [json_string JSONValue];
return parsedResult;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
int i = [indexPath row];
if ( i >= [searchResult count] ) return cell;
NSString *res = [searchResult objectAtIndex:i];
[[cell textLabel] setText:res];
return cell;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// WEB Request
NSArray *entries = [self search:searchString];
numOfRecords = [entries count];
NSMutableArray *entryTitles = [[NSMutableArray alloc] init];
for (int i=0; i<numOfRecords; i++) {
NSDictionary *entry = [entries objectAtIndex:i];
[entryTitles addObject:[entry objectForKey:#"title"]];
}
searchResult = entryTitles;
return YES;
}
The searchResult variable is a member variable of type NSArray. This code works fine, however if I change the assignment of searchResult to
searchResult = [NSArray arrayWithArray: entryTitles];
The program crashes after typing the second letter in the search field with a EXC_BAD_ACCESS.
Can somebody explain what is the problem that causes this error?
You probably just need to retain it:
searchResult = [[NSArray arrayWithArray: entryTitles] retain];
You need to do this because arrayWithArray just creates an autoreleased object that will be released in the near future. You need to add your retain to take ownership.
Once you've taken ownership, don't forget to release it somewhere.
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// WEB Request
NSArray *entries = [self search:searchString];
[searchResult autorelease];
searchResult = [[entries valueForKeyPath:"#unionOfObjects.title"] retain];
return YES;
}
I ran across the same issue. My problem was that in cellForRowAtIndexPath I was releasing the object in the search results array after loading the string value into the cell for display.
Consequently, when I tried to search for anything it immediately crashed.
Check to make sure you aren't releasing the objects that you are searching against.