iOS

Tutorial 2: iOS Shipment Tracker with Reminders

Marcio Valenzuela walks you through Part 2 of the coding process required to create shipment tracking app.

Sometimes the best way to learn is to do the work. When you reach the end of these tutorials you will have a working shipment tracking application for iOS.

Data Model

Now let's switch over to creating the data model. We are looking to create a class object for our shipments, which will give structure to our shipments. Create a new file of Objective C class type and subclassing NSObject and name it Shipment.

In your shipment interface file you will add these two properties:

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSDate *date;

Don't forget to synthesize them in the implementation file. So we are finished with our shipment object. Each shipment will have a name and a date. Now import the Shipment class into your ViewController to be able to use it.

In ViewController replace your NSArray ivar declaration line with this one:

NSMutableArray *tempArray;

We need a mutable array because we will be adding and deleting objects from that array as we modify our tableview. Delete or comment out the NSArray alloc/init line in vDL method and replace it with this:

tempArray = [NSMutableArray arrayWithCapacity:20];
Shipment *shipment = [[Shipment alloc] init];
shipment.name = @"iMacs";
shipment.date = [NSDate date];
[tempArray addObject:shipment];
And replace your cellForRowAtIndexPath (cFRAIP) method with this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellIdentifier"];
}
Shipment *shipmentToDisplay = [tempArray objectAtIndex:indexPath.row];
cell.textLabel.text = shipmentToDisplay.name;
return cell;
}

Notice the lines that changed. In our vDL we create a new array shipment object and add it to our array. We then get back that object from our array and put it into a new Shipment object instance to be used for the tableview.

Delete and Add

Run the app and nothing changed except where you are getting your data. Now keep in mind we are only storing these objects in a local array, which lives in memory so long as the app instance is running. This allows us to manipulate data that is stored temporarily in memory. Let's first implement Delete & Add functionality and then move on to making our data store permanent.

Adding the delete functionality is easiest so let's start with that one. We simply add this method to our ViewController class:

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[tempArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}

Tableviews have these methods already "in them" because it is a very common task to want to erase a table view cell from view. We simply add the call to remove the current object from the data source, tempArray. The tableview takes care of the rest; meaning deleting the cell and re-sorting the cells in order to make them fit properly again.

Moving on to the Add functionality, we need a button. So let's drag a Bar Button Item from the Object Library and drop it onto the navigation bar at the top of our ViewController scene in storyboard. (Figure A)

Figure A

Once we place it on the nav bar, we can modify its properties. The first thing we want to do is make it look like an actual Add button, so select the Attributes Inspector icon on the right hand pane and from the Identifier drop down list, select Add. Your button should now look like Figure B.

Figure B

Now let's connect it to something. In storyboards, a connection from one scene (viewcontroller or object) to the next, is called a segue. In order to segue into another scene however, we need another scene. So let's drag a new TableViewController from the library to the right of the ViewController scene. Once again, Editor | Embed in Navigation Controller and you get a navigation controller in between the two scenes. Your storyboard should look like Figure C.

Figure C

So now we right click the newly added "+" button and Ctrl+Drag from the button to the new navigation controller. When you let go of the button, you get a black pop-up asking you the type of segue. We will select modal, which means the new view controller will pop up from the bottom of the screen. Your storyboard looks like Figure D.

Figure D

Let's add a title to each tableview controller. Name the first one "Shipments" and the second one "Add Shipment". You do this by double clicking on the navigation bar in each and typing in the name.

Notice there is now a connector between Shipments Scene and Add Shipment Scene. That's the modal segue. Go ahead and run the app. When you tap the "+" button, the new scene pops up from below. However, not only do we not have a way to dismiss this new scene, but it's also not associated to any view controller class. Our original scene is of type ViewController and we have an .h/.m file set describing that class. The new scene does not.

A new Scene

First let's add the new Class file. It's still an Objective C Class file but let's subclass UITableViewController directly. (Figure E)

Figure E

I called it AddShipmentVC but notice what XCode did for me? It automatically added the ViewController suffix to it. That's because I told XCode to subclass UITableViewController. If you leave it as an NSObject subclass, it will remove that suffix. It's a handy feature if you pay attention, otherwise it can be a real pain.

Your Project Navigator now looks like Figure F.

Figure F

Let's refactor it before moving on. Right-click over the Class name in AddShipmentVCViewController.h as below and rename it to AddShipmentViewController. (Figure G)

Figure G

Click Preview and XCode presents you with every instance to be renamed so you can choose whether to do so or not. Click Enable Snapshots, which basically keeps a journal of your changes in case you want to revert to a particular one. Now that our class is properly named, let's go to storyboards and make the AddShipment vc of class AddShipmentViewController by selecting the scene and in the Identity Inspector choosing the proper class name. (Figure H)

Figure H

While we are here, drag and drop two Bar Button Items on either side of the Add Shipment title and make the left one of type Cancel and the right one of type Done. Go ahead and make the Cancel button red. (Figure I)

Figure I

Adding the code

Okay now this scene will look to AddShipmentViewController class files for its behavior. We need to add that behavior in code. So make your AddShipmentViewController.h file look like this:

#import <UIKit/UIKit.h>
@class AddShipmentViewController;
@protocol AddShipmentViewControllerDelegate <NSObject>
- (void) addShipmentViewControllerDidCancel:(AddShipmentViewController *)controller;
- (void) addShipmentViewControllerDidSave:(AddShipmentViewController *)controller;
@end
@interface AddShipmentViewController : UITableViewController
@property (nonatomic, weak) id <AddShipmentViewControllerDelegate> delegate;
- (IBAction)cancel:(id)sender;
- (IBAction)done:(id)sender;
@end

This is a mouthful so let's review. There are two @interface-@end blocks. The second one is the typical one you know. The class is named AddShipmentViewController and is of subclass UITableViewController. It has one property and two IBAction methods.

The first block declares a protocol called AddShipmentViewControllerDelegate, which has two methods. This means any class conforming to this protocol has access to these two methods.

So AddShipmentViewController declares a delegate property for that protocol. It means that anyone importing this protocol must declare itself a delegate. That delegate will receive a callback. So we will make our Shipments ViewController conform to this protocol and be its delegate in a minute. For now let's connect the buttons to call these methods. So in AddShipmentViewController storyboards let's select the AddShipmentViewController's Done button and Ctrl+Drag from it to the bottom of the scene to the dock for that vc. Finally we select the appropriate method. (Figure J)

Figure J

Repeat for the Cancel button. Now add the actual methods to the .m file:

#pragma mark - Delegate Button Calls
- (IBAction)cancel:(id)sender
{
[self.delegate addShipmentViewControllerDidCancel:self];
}
- (IBAction)done:(id)sender
{
[self.delegate addShipmentViewControllerDidSave:self];
}

Synthesize the delegate property:

@synthesize delegate;

Now let's use the delegate in our Shipments ViewController class. Switch to the ViewController.h file and make it look like this:

#import <UIKit/UIKit.h>
#import "AddShipmentViewController.h"
@interface ViewController : UITableViewController <AddShipmentViewControllerDelegate>
@end
We are now conforming to the protocol. Finally let's implement the methods we are allowed according to the protocol:
#pragma mark - PlayerDetailsViewControllerDelegate
- (void)addShipmentViewControllerDidCancel:
(AddShipmentViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void) addShipmentViewControllerDidSave:
(AddShipmentViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}

This is great because now we are telling the ViewController that it must dismiss the AddShipmentViewController it presented in the segue via the "+" button in its nav bar.

Sweet! Now let's actually tell the AddShipmentVC that the ViewController is in fact its delegate. We do this by adding this method to ViewController:

#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"AddShipment"])
{
UINavigationController *navigationController = segue.destinationViewController;
AddShipmentViewController *addShipmentViewController = [[navigationController viewControllers] objectAtIndex:0];
addShipmentViewController.delegate = self;
}
}

As the method name implies, this method is called when the segue is being prepared. It checks to see if the segue called is the proper one (because you can have a segue to AddShipment or ModifyShipment etc). If it is, it calls the destinationViewController (which we know is a navigation controller) and uses it to allocate an instance of it. With this instance at hand, we can set its delegate property to self (self being ViewController).

Before we run it, let's name the segue. So in storyboards select the actual segue and name it AddShipment. (Figure K)

Figure K

While you are here, play around with the Transition property to choose which you like best.

Run the app and take a well-deserved break. So far we created a Shipments ViewController and an AddShipmentViewController. The AddShipmentViewController is ready to use. We just need to capture data from the user and then store it in a more permanent way. This is what we will be doing in Part 3.

Also read:

About

My name is Marcio Valenzuela and I am from Honduras. I have been coding in iOS for 5 years and I previously worked on web applications in ASP and PHP/mySQL. I have a few apps up in the app store and am currently working on a couple of Cocos2d games...

0 comments