rb90
rb90

Reputation: 319

iOS table view with data from an NSDictionary

I am new to iOS programming and I need some advice. I want to create a table view with sections to learn how it works. I have some objects from a model class user. I fill data in the table view using dictionaries. The table view is in a view Controller in my storyboard. The code works, but I don't know if this is a good way to handle the data in my table. Is this a semantic mistake to do this like I did?

Please take a Look at my code and give me some advice.

My user object (User.h):

@interface User : NSObject

@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *city;

@end

Dummy function to fill users into an array (in a helper class):

- (NSMutableArray *) createUser
{
    NSMutableArray *userArray = [[NSMutableArray alloc] init];

    User *user1 = [[User alloc] init];
    user1.firstName = @"Donald";
    user1.lastName = @"Duck";
    user1.email = @"[email protected]";
    user1.city = @"Entenhausen";

    User *user2 = [[User alloc] init];
    user2.firstName = @"Micky";
    user2.lastName = @"Maus";
    user2.email = @"[email protected]";
    user2.city = @"Entenhausen";

    User *user3 = [[User alloc] init];
    user3.firstName = @"Daisy";
    user3.lastName = @"Duck";
    user3.email = @"[email protected]";
    user3.city = @"Frankfurt";

    User *user4 = [[User alloc] init];
    user4.firstName = @"Goofy";
    user4.lastName = @"Goof";
    user4.email = @"[email protected]";
    user4.city = @"Berlin";

    User *user5 = [[User alloc] init];
    user5.firstName = @"Some";
    user5.lastName = @"Body";
    user5.email = @"[email protected]";
    user5.city = @"Somewhere";

    User *user6 = [[User alloc] init];
    user6.firstName = @"Dagobert";
    user6.lastName = @"Duck";
    user6.email = @"[email protected]";
    user6.city = @"Mainz";

    [userArray addObject:user1];
    [userArray addObject:user2];
    [userArray addObject:user3];
    [userArray addObject:user4];
    [userArray addObject:user5];
    [userArray addObject:user6];

    return userArray;

}

Function to create a dictionary object (in a helper class):

- (NSMutableDictionary *) createDictionaryFromArray: (NSMutableArray *) allData
{
    NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"Entenhausen", @"Frankfurt", @"Berlin", @"Mainz", nil];

    NSMutableDictionary *collection = [[NSMutableDictionary alloc] init];

    for (int i=0; i < arrayKeys.count; i++) {
        NSString *key = [arrayKeys objectAtIndex:i];
        NSMutableArray *allValues = [[NSMutableArray alloc] init];

        for (User *usr in allData) {

            if([usr.city isEqualToString:key]) {
                [allValues addObject:usr];
            }
        }

        [collection setObject:allValues forKey:key];
    }

    return collection;
}

My table view controller class (TableViewController.h):

#import <UIKit/UIKit.h>
#import "HelperClass.h"

@interface TableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
    HelperClass             *helper;
    NSMutableArray          *users;
    NSArray                 *allKeys;
    NSDictionary            *dictionaryUsers;
}

TableViewController.m:

#import "TableViewController.h"

@implementation TableViewController


- (void)viewDidLoad
{
    [super viewDidLoad];

    helper = [[HelperClass alloc] init];
    users = [[NSMutableArray alloc] init];
    users = [helper createUser];

    dictionaryUsers = [[NSMutableDictionary alloc] init];
    dictionaryUsers = [helper createDictionaryFromArray:users];
    allKeys = [NSArray array];
    allKeys = [dictionaryUsers allKeys];


}


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{    // Return the number of sections.
    return [allKeys count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    NSMutableArray *arrayValuesUsers = [[NSMutableArray alloc] init];
    arrayValuesUsers = [dictionaryUsers objectForKey:[allKeys objectAtIndex:section]];
    return [arrayValuesUsers count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

    return [allKeys objectAtIndex:section];

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    User *user = [[User alloc] init];

    NSMutableArray *usersInSection = [dictionaryUsers objectForKey:[allKeys objectAtIndex:indexPath.section]];
    user = [usersInSection objectAtIndex:indexPath.row];

    cell.textLabel.text = [NSString stringWithFormat:@"%@, %@", user.lastName, user.firstName];
    cell.detailTextLabel.text = user.email;

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSMutableArray *usersInSection = [dictionaryUsers objectForKey:[allKeys objectAtIndex:indexPath.section]];
    User *usr = [usersInSection objectAtIndex:indexPath.row];

    NSLog(@"user: %@, %@", usr.lastName, usr.firstName);
}



@end

Upvotes: 0

Views: 1377

Answers (1)

Muxor
Muxor

Reputation: 171

First of all thumbs up for using a HelperClass for implementing general functions. Only when you do it, make it at least a singleton, so that you not always have to call:

helper = [[HelperClass alloc] init];

Or if you only have functions like createUser, these methods should be class methods.

Here are two general hints, which came to my mind, when looking at your code:

You have too many allocations in your code. For example in your TableViewController you do:

users = [[NSMutableArray alloc] init];
users = [helper createUser];

So first you allocate a new array and the first thing the createUser function does is to allocate another array in memory, which is then returned. Your first allocated memory space is never used.

When it comes to tableViews don't use dictionaries. Always stick with arrays. A dictionary is not ordered and when iterating through a dictionary you might get different results each time. An array is ordered and objectOfIndex:i will always return the same object.

So I adjusted your code to have a dynamic tableView sorting your users into sections by their city and sorting them alphabetically. (I guess this was what you wanted to achieve). The new code is not completely clean. You could try to use NSArray instead of NSMutableArray everywhere to save some memory for example.

Your User.h is untouched.

Here is the new HelperClass.h

//  HelperClass.h


#import <Foundation/Foundation.h>

@interface HelperClass : NSObject


+ (HelperClass *)sharedHelperClass;
+ (NSMutableArray *) createUser;
+ (NSMutableArray *) getAllCitiesFromUserArray: (NSMutableArray *)userArray;
+ (NSMutableArray *)getUsersOfUsersArray: (NSMutableArray *)userArray fromCity: (NSString *)city;

@end

And the HelperClass.m

 //  HelperClass.m


#import "HelperClass.h"
#import "User.h"

@implementation HelperClass


+ (HelperClass *)sharedHelperClass
{

    //This returns always the same object of HelperClass
    static dispatch_once_t pred;
    static HelperClass *_sharedHelperClass = nil;
    dispatch_once(&pred, ^{ _sharedHelperClass = [[self alloc] init]; });
    return _sharedHelperClass;
}


- (id)init{
    self = [super init];
    if (!self) {
        return nil;
    }
    return self;
}


+ (NSMutableArray *) createUser
{
    NSMutableArray *userArray = [[NSMutableArray alloc] init];

    User *user1 = [[User alloc] init];
    user1.firstName = @"Donald";
    user1.lastName = @"Duck";
    user1.email = @"[email protected]";
    user1.city = @"Entenhausen";

    User *user2 = [[User alloc] init];
    user2.firstName = @"Micky";
    user2.lastName = @"Maus";
    user2.email = @"[email protected]";
    user2.city = @"Entenhausen";

    User *user3 = [[User alloc] init];
    user3.firstName = @"Daisy";
    user3.lastName = @"Duck";
    user3.email = @"[email protected]";
    user3.city = @"Frankfurt";

    User *user4 = [[User alloc] init];
    user4.firstName = @"Goofy";
    user4.lastName = @"Goof";
    user4.email = @"[email protected]";
    user4.city = @"Berlin";

    User *user5 = [[User alloc] init];
    user5.firstName = @"Some";
    user5.lastName = @"Body";
    user5.email = @"[email protected]";
    user5.city = @"Somewhere";

    User *user6 = [[User alloc] init];
    user6.firstName = @"Dagobert";
    user6.lastName = @"Duck";
    user6.email = @"[email protected]";
    user6.city = @"Mainz";

    [userArray addObject:user1];
    [userArray addObject:user2];
    [userArray addObject:user3];
    [userArray addObject:user4];
    [userArray addObject:user5];
    [userArray addObject:user6];

    return userArray;

}


+ (NSMutableArray *) getAllCitiesFromUserArray: (NSMutableArray *)userArray{

    NSMutableArray *cityArray = [[NSMutableArray alloc] init];

    for (User *user in userArray) {
        if (![cityArray containsObject: user.city]){
            [cityArray addObject:user.city];
        }
    }

    //Sort the city array alphabetically (localizedCaseInsensitiveCompare even regards Umlaute)
    [cityArray sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

    return cityArray;
}

+ (NSMutableArray *)getUsersOfUsersArray: (NSMutableArray *)userArray fromCity: (NSString *)city{


    NSMutableArray *usersOfCityArray = [[NSMutableArray alloc] init];


    for (User *user in userArray) {
        if ([user.city isEqualToString:city]){
            [usersOfCityArray addObject:user];
        }
    }


    //Sort the array of custom objects by key lastName 
    NSSortDescriptor *sortDescriptor =
    [NSSortDescriptor sortDescriptorWithKey:@"lastName"
                                  ascending:YES
                                   selector:@selector(localizedCaseInsensitiveCompare:)];
    usersOfCityArray = [[usersOfCityArray sortedArrayUsingDescriptors:@[sortDescriptor]]mutableCopy];
    return usersOfCityArray;

}


@end

The new TableViewController.h

//  TableViewController.h


#import <UIKit/UIKit.h>

@interface TableViewController : UITableViewController

@end

And the new TableViewController.m

//  TableViewController.m


#import "TableViewController.h"
#import "HelperClass.h"
#import "User.h"

@interface TableViewController ()

@property (nonatomic,strong) NSMutableArray *users;
@property (nonatomic,strong) NSMutableArray *sectionHeaders;


@end

@implementation TableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];


    //This gets the Array of Users from the HelperClass class method
    //You could access the HelperClass Singleton with [HelperClass sharedHelperClass], but this is not needed in this case.

    self.users = [HelperClass createUser];
    self.sectionHeaders = [HelperClass getAllCitiesFromUserArray:self.users];



}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return [self.sectionHeaders count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    //Get the current section city
    NSString *city = [self.sectionHeaders objectAtIndex:section];
    NSMutableArray *sectionUsers =[HelperClass getUsersOfUsersArray:self.users fromCity:city];

    return sectionUsers.count;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

    return [self.sectionHeaders objectAtIndex:section];

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }


    //Get the current section city
    NSString *city = [self.sectionHeaders objectAtIndex:indexPath.section];
    //Get users for the current city
    NSMutableArray *sectionUsers =[HelperClass getUsersOfUsersArray:self.users fromCity:city];
    //Get the user for the current cell
    User *user = [sectionUsers objectAtIndex:indexPath.row];

    cell.textLabel.text = [NSString stringWithFormat:@"%@, %@", user.lastName, user.firstName];
    cell.detailTextLabel.text = user.email;

    return cell;
}



#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Get the current section city
    NSString *city = [self.sectionHeaders objectAtIndex:indexPath.section];
    //Get users for the current city
    NSMutableArray *sectionUsers =[HelperClass getUsersOfUsersArray:self.users fromCity:city];
    //Get the user for the current cell
    User *user = [sectionUsers objectAtIndex:indexPath.row];

    NSLog(@"user: %@, %@", user.lastName, user.firstName);

}


@end

Upvotes: 1

Related Questions