iOS Programming Recipe 33 – Relational Data Handling With Core Data

Many times with app development there will be a need to store quite a bit of relational information within your app. This could be any data, for example what if you needed to store data about an managers and their employees. Core Data is the perfect solution to these types of problems. In this recipe, We’ll walk you through setting up an application that utilizes core data.

Assumptions

  • You are familiar with creating projects and the App Delegate
  • You are comfortable creating classes and subclasses
  • You have worked with relational databases in the past
  • You have a grasp for building interfaces
  • You are amazing at making outlets and actions

What is Core Data?

First off, let’s start by briefly explaining what core data is. Core data is a framework that provides a layer of abstraction between relational data and the way you interface with it. So what’s that mean? Basically, it allows you to work with objects instead of columns and tables as you would with PHP or other similar programming languages.

Due to the fact that Core Data provides a layer of abstraction, The database store can be of many different types such as SQLite, XML, and binary. The most commons and therefore default, is SQLite, so that’s what we’ll be using here.

I know this all sounds a bit confusing, but hopefully the rest of this recipe will clear it up a bit.

Setting Up The Project

Ok, to start with , create a new empty application. You can title it “Company” as we will be creating a simple app to store information about a company. Make sure you check the “Use Core Data” box as shown in Figure 1.

FIgure_1

Figure 1: Creating a new empty Application

Creating The Data Model

Now most companies have managers and employees, So we’ll start by creating a data model that contains both. Start by opening the Company.xcdatamodeld file. You should be presented with a view that looks like Figure 2.

Figure_2

Figure 2: A view of the Core Data model editor

Now add two new entities by pressing the “+” button in the bottom left corner of the data model window. Rename them to “Manager” and “Employee” respectively as shown in Figure 3.

Figure_3

Figure 3: A view of the renamed entities

Next, we’ll want to add some attributes. Both managers and employees have names and titles. For example John Doe and Software Engineer. Select one of the entities and add a new attribute by pressing the “+” button under the attributes section. Name the first one “name” and give it a string type. Repeat this process to create a “title” attribute for the employee. When you’re done, you’re employee and manager attributes should look like Figure 4.

FIgure_4

Figure 4: An entity with name and title string type attributes

In most businesses, managers can have many employees. So we’ll want to create this relationship in our data model. If you haven’t already guessed it, this means we’ll want to create a new relationship. Since this isn’t “Office Space” we’re talking about here, we’ll assume an employee only has one manager. Hence, we’ll want to create a “To Many” relationship between the manager and the employee as shown in Figure 5. Here we created the relationship and gave it a name “employees” and a destination “Employee”. Then from the data model inspector we chose the “To Many” from Type drop-down.

Figure_5

Figure 5: Setting up the manager relationships

If you switch over to the Graph editor style by selecting the button in the bottom right corner of the editor window, you can see the “To Many” relationship is indicated by the double arrows. Figure_6 shows this representation.

Figure_6

Figure 6: The manager-employee relationship in graph view

Before we go on, It’s usually a good idea to create the inverse relationship. Select the “Employee” entity and create a relationship to the Manager with the name “manager”, destination “Manager”, and inverse “Employee” as shown in Figure 7. Make sure you select a “To One” relationship as well. Although, the system will usually create this inverse relationship for you, there is a potential for data integrity problems if you skip this step. Once you are done, you may also notice that if you select the “Manager” entity, the inverse relationship has now been selected for you.

Figure_7

Figure 7: Setting up the employee relationships

Although very simple, the data model is complete. Now lets switch focus and start using this data model for something. We can first start by creating interfaces for creating these data objects. Before we do that though, Navigate to the editor menu and choose “Create NSManagedObject Subclass…”. The first screen you are presented with doesn’t matter much, so just press the next button. On the Second screen, make sure you select both Manager and Employee as shown in Figure 8. Again press next.

Figure_8

Figure 8: Selecting entities for creation of the NSManagedObject subclasses

What you have just done is create two new objects that are linked to Core Data. If you look at the Manager.h file. You’ll notice (Code Chunk 1) some code has been created which allows you to select employees as well as do some operations such as “add”.

Code Chunk 1: The automatically generated NSManagedObject for the Manager Entity


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>;


@interface Manager : NSManagedObject

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSSet *employees;
@end

@interface Manager (CoreDataGeneratedAccessors)

- (void)addEmployeesObject:(NSManagedObject *)value;
- (void)removeEmployeesObject:(NSManagedObject *)value;
- (void)addEmployees:(NSSet *)values;
- (void)removeEmployees:(NSSet *)values;

@end

Creating The Interfaces

We’ll start by creating interfaces that will allow the creation of both employees and managers. All-in-all, you will need 4 classes in addition to the two created for the Core Data model. You’ll need two UIViewControllers and two UITableViewControllers. Title them “RoleCreationViewController”,”AssignEmployeeViewController”,”ManagerTableViewController”, and “EmployeeTableViewController” respectively. Make sure you select “With XIB for user interface” for the two view controllers.

Designing The RoleCreationViewController Class Interface

The RoleCreationViewController class will have 3 labels, 2 text fields, 1 segmented control, and a save button. Arrange them as shown in Figure 9.

Figure_9

Figure 9: The completed RoleCreationViewController interface

Create the following outlets and respective names:

  • name label – personName;
  • title label – personTitle;
  • segmented control – personRoleType;

For the save button, create an action with the title “savePerson”.

None of these outlets or actions need to be visible to other classes, so define them inside the .m file as shown in Code Chunk 2. Also, go ahead and add the UITextFieldDelegate protocol, as we will need this later to dismiss the keyboard.

Code Chunk 2: The completed RoleCreationViewController.m file interface


1
2
3
4
5
6
7
8
@interface RoleCreationViewController () <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *personName;
@property (weak, nonatomic) IBOutlet UITextField *personTitle;
@property (weak, nonatomic) IBOutlet UISegmentedControl *personRoleType;

- (IBAction)savePerson:(id)sender;

@end

Designing The AssignEmployeeViewController Class Interface

The next class interface will have two labels, a UITableView, and a button. Arrange them as shown in Figure 10 and create the following Outlets and Actions:

  • Manager label – managerName
  • Table view – tableView
  • Add Employees button (action) – addEmployees

You can show the navigation and tab bar controller by selecting them in drop-downs in the attributes inspector. You will probably want to do this for the RoleCreationViewController interface as well. This will require you to rearrange the objects a little.

Figure_10

Figure 10: The complete AssignEmployeeViewController interface

Again, all of these outlets and actions can stay within the .m file as shown in Code Chunk 3. Since we will be using a table view, make sure you include the UITableViewDataSource protocol declaration as well.

Code Chunk 3: The complete AssignEmployeeViewController.m file


1
2
3
4
5
6
7
8
9
@interface AssignEmployeeViewController () <UITableViewDataSource>

@property (weak, nonatomic) IBOutlet UILabel *managerName;
@property (strong, nonatomic) Manager *manager;

@property (weak, nonatomic) IBOutlet UITableView *tableView;

- (IBAction)addEmployees:(id)sender;
@end

Coding The RoleCreationViewController Class

Since we’ll want to retrieve the Core Data context often, it makes good sense to create a custom initializer for our class that will accept the Core Data context. Go ahead and modify the RoleCreationViewController.h class as shown in Code Chunk 4. This will let other classes know that we’re using a custom initializer. We’ll revisit this soon.

Code Chunk 4: Declaring the custom initializer in the RoleCreationViewController.h file


1
2
3
4
5
6
7
#import <UIKit/UIKit.h>

@interface RoleCreationViewController : UIViewController

-(id)initWithManagedObjectContext:(NSManagedObjectContext *)context;

@end

Now let’s complete the RoleCreationViewController.m interface. Code Chunk 5 shows that we first import both the Manager and Employee classes. Then we define a constant, “EMPLOYEE”, with the integer value 0. Lastly, we set properties for both an NSManagedObjectContext object and an NSFetchedResultsController. These properties will be necessary for both retrieving and accessing Core Data objects. We’ll use these imported classes as well as the integer definition soon.

Code Chunk 5 Setting up imports, definitions, and additional properties in the RoleCreationViewController.m interface


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import "RoleCreationViewController.h"
#import "Manager.h"
#import "Employee.h"

#define EMPLOYEE 0

@interface RoleCreationViewController () <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *personName;
@property (weak, nonatomic) IBOutlet UITextField *personTitle;
@property (weak, nonatomic) IBOutlet UISegmentedControl *personRoleType;

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

- (IBAction)savePerson:(id)sender;

@end

Replace the existing initializer shown in Code Chunk 6 with the new one (Code Chunk 7) that we declared in the header file. This new initializer will set the NSManagedObjectContext with the context retrieved from the instantiation of the class. Later we’ll handle the instantiation of this class from the application delegate.

Code Chunk 6: The unneeded old initializer that you will replace


1
2
3
4
5
6
7
8
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

Code Chunk 7: The new initializer


1
2
3
4
5
6
7
8
9
10
11
-(id)initWithManagedObjectContext:(NSManagedObjectContext *)context

{
    self = [super init];
    if (self)
    {
        self.managedObjectContext = context;

    }
    return self;
}

Since we’ll need to dismiss the keyboards, we’ll want to set both of the text field delegates to “SELF”. This will assign responsibility for handling text field events to the RoleCreationViewController class.

Code Chunk 8: Setting the text field delegates to the RoleCreationViewController class


1
2
3
4
5
6
7
- (void)viewDidLoad
{
    [super viewDidLoad];

    self.personName.delegate = self;
    self.personTitle.delegate = self;
}

In order to dismiss the keyboards, we’ll want to implement a delegate method to handle the return event that occurs when you press the return button on the keyboard. Add the method shown in code Chunk 9 to the RoleCreationViewController.m file.

Code Chunk 9: Implementing the textFieldShouldReturn delegate method.


1
2
3
4
5
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return NO;
}

Finally, we’ll want to implement the method for handling the “save” action. This is a fairly large implementation, so we’ll show you the code (Code Chunk 10) and then explain it.

Code Chunk 10: The full savePerson method implementation


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- (IBAction)savePerson:(id)sender
{
    if(self.personName && self.personTitle && self.personRoleType)
    {
        if(self.personRoleType.selectedSegmentIndex == EMPLOYEE)
        {
            NSEntityDescription *employeeEntityDescription = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.managedObjectContext];


            Employee *newEmployee = (Employee *)[[NSManagedObject alloc] initWithEntity:employeeEntityDescription
                                                         insertIntoManagedObjectContext:self.managedObjectContext];

            newEmployee.name = self.personName.text;
            newEmployee.title = self.personTitle.text;

        }
        else
        {

            NSEntityDescription *managerEntityDescription = [NSEntityDescription entityForName:@"Manager" inManagedObjectContext:self.managedObjectContext];


            Manager *newManager = (Manager *)[[NSManagedObject alloc] initWithEntity:managerEntityDescription
                                                         insertIntoManagedObjectContext:self.managedObjectContext];

            newManager.name = self.personName.text;
            newManager.title = self.personTitle.text;


        }

    }
    else
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Missing Information" message:@"Hey man you totally forgot to input some information!" delegate:nil cancelButtonTitle:@"Got It!" otherButtonTitles:nil];
        [alertView show];


    }

}

This method contains two if-else statements. The outer if-else statement checks to see if there are values for the two text fields or the segmented control. If there are no values for these, it will present the user with an alert saying so.

If there ARE values for these three input fields, then we’ll check wether or not the selected role type is an employee (defined as integer 0 above). If it is an employee, then we’ll move forward by finding the Employee entity, Creating an employee object, inserting the employee in the Core Data model and setting the employee’s name and title. The steps for a manager are identical.

NOTE: It’s probably a better practice to refactor this code so we don’t repeat somewhat identical code, but this way is a bit more instructive so we’ll leave it how it is.

That completes the RoleCreationViewController, next we’ll turn our attention over to the AssignEmployeeViewController class.

Coding The AssignEmployeeViewController Class

As we did with the RoleCreationViewController class, We’ll want to add the NSManagedObjectContext and NSFetchedResultsController properties to the .m file interface as shown in Code Chunk 11.

Code Chunk 11: Adding the Necessary Core Data properties to the .m interface


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import "AssignEmployeeViewController.h"
#import "Employee.h"
#import "EmployeeTableViewController.h"

@interface AssignEmployeeViewController () <UITableViewDataSource>

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (weak, nonatomic) IBOutlet UILabel *managerName;
@property (strong, nonatomic) Manager *manager;

@property (weak, nonatomic) IBOutlet UITableView *tableView;

- (IBAction)addEmployees:(id)sender;
@end

We will also need to create a custom initializer again, so modify the .h file and the .m file as shown in Code Chunk 12. This time, our initializer will take both a context and a manager object as inputs.

Code Chunk 12: Replacing the initializer with the custom initializer

AssignEmployeeViewController.h


1
2
3
4
5
6
7
8
#import <UIKit/UIKit.h>
#import "Manager.h"

@interface AssignEmployeeViewController : UIViewController

-(id)initWithManager:(Manager *)manager andManagedContext:(NSManagedObjectContext *)context;

@end

AssignEmployeeViewController.m


1
2
3
4
5
6
7
8
9
10
11
12
-(id)initWithManager:(Manager *)manager andManagedContext:(NSManagedObjectContext *)context

{
    self = [super init];
    if (self)
    {
        self.managedObjectContext = context;
        self.manager = manager;

    }
    return self;
}

The viewDidLoad method will have to be modified to set the tableView data source as well as set the name of the manager. Code Chunk 13 shows this modification.

Code Chunk 13: Setting the tableView datasource as well as the manager name text label.


1
2
3
4
5
6
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.tableView.dataSource = self;
    self.managerName.text = self.manager.name;
}

Now we need to add the tableView datasource protocol methods. The tableView will show the employees that belong to the manager. First, we’ll need the tableView:numberOfRowsInSection: method shown in Code Chunk 14. This method will take the number of employees that belong to the manager and set the number of rows equal to that amount.

Code Chunk 14: Implementing the


1
2
3
4
5
6
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    return self.manager.employees.count;

}

We will also need the table view: cellForRowAtIndexPath: method to actually populate the table view rows. We’ll do that by first creating a new cell for each employee, then we’ll set the cell title and subtitle to that employee’s name and title. *code Chunk 15** shows this full method.

Code Chunk 15: Implementing the tableView:cellForRowAtIndexPath method


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    static NSString *CellIdentifier = @"EmployeeCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];

    }

    Employee *employee = [self.manager.employees.allObjects objectAtIndex:indexPath.row];

    cell.textLabel.text = employee.name;
    cell.detailTextLabel.text = employee.title;

    return cell;

}

Now we need to fill in the “addEmployees” action. This is fairly simple. Basically we will create a new instance of the EmployeeTableViewController and present it onto the navigation controller stack. This will allow us to select an employee to add to the manager in question. Since we’ll want to assign the employee to a manager, it’s a good idea to pass the manager object into the EmployeeTableViewController when we initialize it. This will obviously require another custom initializer, which we’ll implement shortly. For now fill in the “addEmployee” method with the code shown in Code Chunk 16. Notice, that we also have a navigation controller we are taking advantage of. Problem is, we haven’t implemented a navigation controller yet. We will do that later inside the Application Delegate.

Code Chunk 16: Implementing the addEmployees action method


1
2
3
4
5
6
7
- (IBAction)addEmployees:(id)sender
{
    EmployeeTableViewController *employeeTableViewController = [[EmployeeTableViewController alloc] initWithManager:self.manager andManagedContext:self.managedObjectContext];

    [self.navigationController pushViewController:employeeTableViewController animated:YES];

}

One final touch for this class will be to add a viewDidAppear method to the AssignEmployeeViewController.m file to ensure that the table view gets reloaded every time the view becomes visible.Code Chunk 17 shows this step

Code Chunk 17: Adding the viewDidLoad method


1
2
3
4
- (void)viewDidAppear:(BOOL)animated
{        
[self fetchManagers];
}

Now, we’ll need to set up the two table view controllers. The Manager table view controller will be on a new tab. Once you select a manager from this table view controller it will bring up the assignEmployeeViewController. From there you can press “Add Employee” and a second table view controller will be presented with a list of Employees.

Let’s implement the ManagerTableViewController now.

Implementing the ManagerTableViewController

For this tableViewController we’ll also need a custom initializer, so we’ll start with that. Code Chunk 18 shows the .m and .h modifications. Again, you will want to replace the existing init method.

Code Chunk 18: Implementing the custom initializer

MangerTableViewController.h


1
2
3
4
5
6
7
#import <UIKit/UIKit.h>

@interface ManagerTableViewController : UITableViewController

- (id) initWithManagedObjectContext:(NSManagedObjectContext *)context;

@end

ManagerTableViewController.m


- (id)initWithManagedObjectContext:(NSManagedObjectContext *)context
{
    self = [super initWithStyle:UITableViewStylePlain];
    if(self)
    {
        self.managedObjectContext = context;
    }

    return self;
}

Next we’ll make set up the imports and the Core Data properties as shown in Code Chunk 19. Make these changes in the ManageTableViewController.m file.

Code Chunk 19: Adding the necessary imports and Core Data properties


1
2
3
4
5
6
7
8
9
10
#import "ManagerTableViewController.h"
#import "Manager.h"
#import "AssignEmployeeViewController.h"

@interface ManagerTableViewController ()

@property (strong, nonatomic)NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic)NSFetchedResultsController *fetchedResultsController;

@end

In order to retrieve the managers from Core Data, we’ll need to create a method for doing so. We’ll create a new method titled “fetchManagers”. This method will create a new request, Set the fetchedResultsController property parameters, and then perform the fetch using the request. Code Chunk 20 Shows this implementation.

Code Chunk 20: Implementing the fetchManagers method


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)fetchManagers
{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Manager"];
    NSString *cacheName = @"ManagerCache";

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    [fetchRequest setSortDescriptors:@[sortDescriptor]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];
    NSError *error;
    if(![self.fetchedResultsController performFetch:&amp;error])
    {

        NSLog(@"Fetch Failure: %@",error);
    }

    [self.tableView reloadData];
}

As with any table view, well need to implement the data source protocol methods. The numberOfSectionsInTableView: method should just return a value of one since we’ll only have one section. The tableView: numberOfRowsInSection: method will simply return the number of objects in the fetchedResultsController property. The tableVIew: CellForRowAtIndexPath method first creates a new cell if on isn’t already made, then it populates the the title and subtitle of the cell with the manager name and title. This is very similar to the way we populate the table view in the AssignEmployeeViewController class. Code Chunk 21 shows the complete implementation of all of these data source methods.

Code Chunk 21: The necessary table view data source methods


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (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.fetchedResultsController.fetchedObjects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ManagerCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

    Manager *manager = (Manager *)[self.fetchedResultsController objectAtIndexPath:indexPath];

    cell.textLabel.text = manager.name;
    cell.detailTextLabel.text = manager.title;

    return cell;
}

In order to make sure everything in the table view loads appropriately, modify the viewDidLoad method and add a viewDidAppear method as shown in Code Chunk 22. Notice we’re also setting the view controller title here as well.

Code Chunk 22: Adding a viewDidAppear method and modifying the viewDidLoad method


1
2
3
4
5
6
7
8
9
10
11
12
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Managers";
    [self fetchManagers];

}
- (void)viewDidAppear:(BOOL)animated
{

        [self fetchManagers];
}

The last step for the MangerTableViewController is to implement the table view delegate method that will allow us to move to the AssignEmployeeViewController. In this method, we basically choose the manager for which cell was tapped and initialize the AssignEmployeeViewController class with that manager before we push it on to the navigation stack. Code Chunk 23 shows this full implementation.

Code Chunk 23: Implementing the table view delegate method, tableView: didSelectRowAtIndexPath


1
2
3
4
5
6
7
8
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    Manager *manager = (Manager *)[self.fetchedResultsController objectAtIndexPath:indexPath];
    AssignEmployeeViewController *assignEmployeeViewController = [[AssignEmployeeViewController alloc] initWithManager:manager andManagedContext:self.managedObjectContext];

    [self.navigationController pushViewController:assignEmployeeViewController animated:YES];
}

That’s it for the ManagerTableViewController. Now we’ll turn our attention over to the EmployeeTableViewController.

Implementing the EmployeeTableViewController

Since the EmployeeTableViewController is nearly identical to the ManagerTableViewController, I’ll just put the full code down here. The only difference is in the table view delegate method. Instead of presenting a view controller initialized with an entity, we’re simply adding the employee to the manager property. The manager property is again set by the custom initializer. Code Chunk 24 shows the EmployeeTableViewController class in it’s entirety.

Code Chunk 24: The entire EmployeeTableViewController class

EmployeeTableViewController.h


1
2
3
4
5
6
7
8
#import <UIKit/UIKit.h>
#import "Manager.h"

@interface EmployeeTableViewController : UITableViewController

-(id)initWithManager:(Manager *)manager andManagedContext:(NSManagedObjectContext *)context;

@end

EmployeeTableViewController.m


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#import "EmployeeTableViewController.h"
#import "Manager.h"
#import "Employee.h"

@interface EmployeeTableViewController ()

@property (strong, nonatomic) Manager *manager;

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

- (void)fetchEmployees;

@end

@implementation EmployeeTableViewController

-(id)initWithManager:(Manager *)manager andManagedContext:(NSManagedObjectContext *)context

{
    self = [super init];
    if (self)
    {
        self.managedObjectContext = context;
        self.manager = manager;

    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Employees";
    [self fetchEmployees];

}

#pragma mark - Table view data source

- (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.fetchedResultsController.fetchedObjects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"EmployeeCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

    Employee *employee = [self.fetchedResultsController.fetchedObjects objectAtIndex:indexPath.row];
    cell.textLabel.text = employee.name;
    cell.detailTextLabel.text = employee.title;

    return cell;
}


- (void)fetchEmployees
{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    NSString *cacheName = @"EmployeeCache";

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    [fetchRequest setSortDescriptors:@[sortDescriptor]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];
    NSError *error;
    if(![self.fetchedResultsController performFetch:&amp;error])
    {

        NSLog(@"Fetch Failure: %@",error);
    }

    [self.tableView reloadData];
}


#pragma mark - Table view delegate

// In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{


    Employee *employee = (Employee *)[self.fetchedResultsController objectAtIndexPath:indexPath];

    [self.manager addEmployeesObject:employee];


    [self.navigationController popViewControllerAnimated:YES];

}



@end

In order to get all of the classes to work together, we’ll need to update the application delegate.

Updating the App Delegate

Now most of everything you needed for Core Data was created for you, but you do want to import a couple of the view controllers, set the tab bar controllers as well as navigation controllers, and finally set the root view controller. Code Chunk 25 shows the imports needed as well as the necessary changes to the application: didFinishLaunchingWithOptions: method.

Code Chunk 25: Updating the App Delegate


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#import "AppDelegate.h"
#import "RoleCreationViewController.h"
#import "ManagerTableViewController.h"

@implementation AppDelegate

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.


    RoleCreationViewController *roleCreationViewController = [[RoleCreationViewController alloc] initWithManagedObjectContext:self.managedObjectContext];
    [roleCreationViewController setTitle:@"Create Roles"];

    ManagerTableViewController *managerTableViewController = [[ManagerTableViewController alloc] initWithManagedObjectContext:self.managedObjectContext];
    [managerTableViewController setTitle:@"Managers"];


    UINavigationController *managerNavigationController = [[UINavigationController alloc] initWithRootViewController:managerTableViewController];


    UITabBarController *tabBarController = [[UITabBarController alloc] init];
    tabBarController.viewControllers = @[roleCreationViewController,managerNavigationController];

    self.window.rootViewController = tabBarController;

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
//...

Now the application should work as expected. Compile and run the application and you should see something like Figure 11 on the first screen. Navigate through the app and you should be able to create employees as well as assign employees to managers.

Figure_11

That’s it for this tutorial. I know this tutorial was particularly long winded, but I didn’t see any other way around it.

Good Luck!

You can find the source code for this example at the following link: Recipe 33 Source Code

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. How is the fetchManger in viewDidAppear of AssignEmployeeViewController being referred

  2. Hey the alertView did not show up, so i modified the following method

    – (IBAction)savePerson:(id)sender
    {
    if([self.personName.text length] && [self.personTitle.text length] && self.personRoleType)
    {

    }
    else
    {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@”Missing Information” message:@”Hey man you totally forgot to input some information!” delegate:nil cancelButtonTitle:@”Got It!” otherButtonTitles:nil];
    [alertView show];
    }

    }

Speak Your Mind

*

css.php
Privacy Policy