Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Populate UITableView with JSON array

I'm working in Storyboard but I presume now it's time to face the code...

I have a PHP file on my server which outputs the contents of my MySQL database table as an array in JSON format:

{
"id":"2",
"name":"The King's Arms",
"city":"London",
"day":"Tuesday",
}

I'll need all the data eventually, but for now I just want to output the city fields as a UITableView. How would I go about doing this?

like image 392
Sebastian Avatar asked Jan 18 '14 17:01

Sebastian


2 Answers

I believe it was in the year 2012/2013 where Apple covered a fantastic video on code practices, one sub topic highlighted was a good smart way of handling JSON objects and creating data objects for them. I'm sorry I forgot the name of the actual video, if someone remembers it please do edit the answer for me.

What apple covered was to have a data object that stores each json object. We will then create an array to store these objects and access the required fields appropriately when populating our tableView. So in your case you would do something like this.

in your project navigator add a file of NSObject type and add the following code:

#import <Foundation/Foundation.h>
@interface PlaceDataObject : NSObject

-(id)initWithJSONData:(NSDictionary*)data;

@property (assign) NSInteger placeId;
@property (strong) NSString *placeName;
@property (strong) NSString *placeCity;
@property (strong) NSString *placeDay;

@end

and in your .m file you would add this code

#import "PlaceDataObject.h"
@implementation PlaceDataObject
@synthesize placeId;
@synthesize placeName;
@synthesize placeCity;
@synthesize placeDay;

-(id)initWithJSONData:(NSDictionary*)data{
    self = [super init];
    if(self){
        //NSLog(@"initWithJSONData method called");
        self.placeId = [[data objectForKey:@"id"] integerValue];
        self.placeName =  [data objectForKey:@"name"];
        self.placeCity = [data objectForKey:@"city"];
        self.placeDay = [data objectForKey:@"day"];
    }
    return self;
}
@end

What you have now is a data object class which you can use everywhere in your code where ever required and grab the appropriate details for whichever table youre showing, whether it be a city fields table or a city and name table etc. By doing this you will also avoid having json decoding code everywhere in your project. What happens when the name of your 'keys' changes? rather than scouring through your code correcting all your keys, you simply go to the PlaceDataObject class and change the appriopriate key and your application will continue working.

Apple explains this well:

"Model objects represent special knowledge and expertise. They hold an application’s data and define the logic that manipulates that data. A well-designed MVC application has all its important data encapsulated in model objects.... they represent knowledge and expertise related to a specific problem domain, they tend to be reusable."

Populating your array with custom objects for every json entry that comes in from the server

Now onto populating an array of this custom data object you've made. Now following the MVC approach, it's probably best that you have all your methods that process data in a different class, your Model class. That's what Apple recommends to put these kind of methods in a seperate model class where all the processing happens.

But for now we are just going to add the below code to the View Controller just for demonstration purposes.

Create a method in your view controller's m file that will process your JSON array.

//make sure you have a global NSMutableArray *placesArray in your view controllers.h file.
-(void)setupPlacesFromJSONArray:(NSData*)dataFromServerArray{
     NSError *error;
     NSMutableArray *placesArray = [[NSMutableArray alloc] init];
     NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromServerArray options:0 error:error];

     if(error){
         NSLog(@"error parsing the json data from server with error description - %@", [error localizedDescription]);
     } 
     else {
         placesArray = [[NSMutableArray alloc] init];
         for(NSDictionary *eachPlace in arrayFromServer)
         {
             PlaceDataObject *place = [PlaceDataObject alloc] initWithJSONData:eachPlace];
             [placesArray addObject:place];
         }

         //Now you have your placesArray filled up with all your data objects
     }
 }

And you would call the above method like so:

//This is not what your retrievedConnection method name looks like ;)
// but you call the setupPlacesFromJSONArray method inside your connection success method
-(void)connectionWasASuccess:(NSData *)data{
    [self setupPlacesFromJSONArray:data];
}

Populating your tableView with your custom Data objects

As for populating your data in your TableView you do so like this:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
     //We check against table to make sure we are displaying the right number of cells
     // for the appropriate table. This is so that things will work even if one day you 
     //decide that you want to have two tables instead of one.
     if(tableView == myCitysTable){
          return([placesArray count]);
     }
     return 0;
}


 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
     if(cell)
     {
          //set your configuration of your cell
     }
     //The beauty of this is that you have all your data in one object and grab WHATEVER you like
     //This way in the future you can add another field without doing much.

     if([placesArray count] == 0){
         cell.textLabel.text = @"no places to show";
     }
     else{
         PlacesDataObject *currentPlace = [placesArray objectAtIndex:indexPath.row];
         cell.textLabel.text = [currentPlace placeCity];
         // in the future you can grab whatever data you need like this
         //[currentPlace placeName], or [currentPlace placeDay];
     }    
     return(cell);
}

Short disclaimer: the code above has not been tested, but please let me know if it all works well or if I've left out any characters.

like image 148
Pavan Avatar answered Oct 08 '22 07:10

Pavan


If the raw data from you server arrives in a NSData object, you can use NSJSONSerialization class to parse it for you.

That is:

 NSError *error;
 NSMutableArray *cityArray = [[NSMutableArray alloc] init];
 NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromServer options:0 error:error];
 if(arrayFromServer)
 {
     NSLog(@"error parsing data - %@", [error localizedDescription]);
 } else {
    for(NSDictionary *eachEntry in arrayFromServer)
    {
         NSString *city = [eachEntry objectForKey:@"city"];

         [cityArray addObject: city];
    }
 }

Once you're done populating your array of cities, this is what you can return in the table view's data source methods:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1; // your table only has one section, right?
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
     if(section == 0)
     {
          return([cityArray count]);
     }
     NSLog(@"if we're at this place in the code, your numberOfSectionsInTableView is returning something other than 1");
     return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     // are you using standard (default) cells or custom cells in your storyboard?
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
     if(cell)
     {
          cell.text = [cityArray objectAtIndex:indexPath.row];
     }
     return(cell);
}
like image 37
Michael Dautermann Avatar answered Oct 08 '22 07:10

Michael Dautermann