Basic Security in iOS 5 – Part 2
This is a post by Chris Lowe, an iOS application developer and aspiring game developer living in San Antonio, Texas. Welcome to Part Two of the epic tutorial series on implementing basic security in iOS 5! In Part One, we began work on Christmas Keeper, an app designed to keep track of holiday gift ideas. […] By Chris Lowe.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Basic Security in iOS 5 – Part 2
40 mins
Santa Claus is Coming to Town!
Now that we have our data model, table cell, and detail view controllers implemented, we can turn our attention back to the Christmas Present Table and start hooking things up for the big finale.
Create a new file with the iOS\Cocoa Touch\UIViewController subclass template, enter ChristmasListTableViewController for the class, make it a subclass of UITableViewController, and leave both checkboxes unchecked.
Then replace ChristmasListTableViewController.h with the following:
#import
#import "AddChristmasItemViewController.h"
#import "ChristmasItems.h"
@interface ChristmasListTableViewController : UITableViewController
@property (nonatomic, strong) ChristmasItems *christmasGifts;
@property (nonatomic, strong) NSIndexPath *selectedRow;
@end
Here we are going to use the model that we created (ChristmasItems) to easily keep track of presents. We also need to save the selectedRow to pass forward to our detail view that you’ll see in a minute.
Next switch to ChristmasListTableViewController.m and replace it with the following:
#import "ChristmasListTableViewController.h"
#import "ChristmasListTableViewCell.h"
#import "ChristmasDetailsTableViewController.h"
@implementation ChristmasListTableViewController
@synthesize christmasGifts, selectedRow;
#pragma mark - UIStoryBoardSegue Method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"AddChristmasPresent"]) {
// Set the delegate for the modal thats about to present
UINavigationController *navigationController = segue.destinationViewController;
AddChristmasItemViewController *playerDetailsViewController = [[navigationController viewControllers] objectAtIndex:0];
playerDetailsViewController.delegate = self;
} else if ([[segue identifier] isEqualToString:@"ChristmasDetailsSegue"]) {
// Pass-forward the details about the present
ChristmasDetailsTableViewController *playerDetailsViewController = segue.destinationViewController;
playerDetailsViewController.textHolder = [self.christmasGifts textForPresentAtIndex:self.selectedRow];
playerDetailsViewController.presentImageName = [self.christmasGifts imageNameForPresentAtIndex:self.selectedRow];
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.christmasGifts = [[ChristmasItems alloc] initWithJSONFromFile:@"christmasItems.json"];
// Register for the Data Protection Notifications (Lock and Unlock).
// ** NOTE ** These are only enforced when a user has their device Passcode Protected!
// Data Protection is not available in the Simulator
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceWillLock) name:UIApplicationProtectedDataWillBecomeUnavailable object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceWillUnLock) name:UIApplicationProtectedDataDidBecomeAvailable object:nil];
}
// We still must manually remove ourselves from observing.
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Data Protection Testing Methods
// This method will get called when the device is locked, but checkKey will not. It is queued until the file becomes available again.
// I've seen very sporadic results with iOS 5 as to whether this method executes.
- (void)deviceWillLock
{
NSLog(@"** Device is will become locked");
[self performSelector:@selector(checkFile) withObject:nil afterDelay:10];
}
- (void)deviceWillUnLock
{
NSLog(@"** Device is unlocked");
[self performSelector:@selector(checkFile) withObject:nil afterDelay:10];
}
- (void)checkFile
{
NSLog(@"** Validate Data Protection: checkFile");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *jsonPath = [documentsDirectory stringByAppendingPathComponent:@"christmasItems.json"];
if ([[NSFileManager defaultManager] fileExistsAtPath:jsonPath]) {
NSData *responseData = [NSData dataWithContentsOfFile:jsonPath];
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:nil];
NSLog(@"** FILE %@", json);
}
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.christmasGifts.christmasList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ChristmasListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ChristmasListCell"];
cell.textView.text = [[self.christmasGifts.christmasList objectAtIndex:indexPath.row] objectForKey:@"text"];
[cell.thumbnail setImage:[self.christmasGifts imageForPresentAtIndex:indexPath]];
return cell;
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedRow = indexPath; // Save the selected indexPath so that the prepareForSegue method can access the right data
return indexPath;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.christmasGifts removeGiftAtIndexPath:indexPath];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
#pragma mark - AddChristmasItemViewController Delegate Callback
-(void)addChristmasItemToList:(NSDictionary *)item
{
[self.christmasGifts addPresentToChristmasList:item];
[self.tableView reloadData];
}
@end
Another fairly straight-forward class, thanks to the awesome power of iOS 5!
Let’s first take a close look at viewDidLoad. Here we are instantiating our model class with the JSON file we specified. Then we are registering for the two Data Protection API methods called “UIApplicationProtectedDataWillBecomeUnavailable” and “UIApplicationProtectedDataDidBecomeAvailable.”
In this case, we aren’t doing anything security-wise, we are merely calling the dummy methods “deviceWillLock” and “deviceWillUnlock.” However, you can do anything you need to here. You may want to delete files, log the user out, prompt them for credentials when the device is unlocked again, etc.
Also, because we are ARC-enabled, we don’t typically have a dealloc method, but in this case it’s needed. This is because we have to remove ourselves from listening for the Data Protection notifications when the instance of our class is released.
Note: The data protection APIs are not available in the Simulator, so you must be on device for this test to work.
Moving down to the Table View Delegate methods, you can see that we are accessing our array in the ChristmasItems model to return the count. This is followed by dequeueing a reusable cell from our table view.
You may wonder where all of my code went for if(cell == nil), etc. In iOS 5, Apple has said “dequeue a cell and if one exists you’ll get it, otherwise we’ll create a new one for you!” Sweet! This simplifies and removes code so that we now only have to deal with the contents of the cell directly (in our case, the textView and thumbnail).
In the commitEditingStyle method, we are utilizing those helper methods we created in the ChristmasItems class to remove the gift. Then we update the table view.
You can also see that we implemented our delegate method for the AddChristmasItemViewControllerDelegate. This allows us to add new presents passed in by the user.
Lastly, in our prepareForSegue method we are checking for the Segue from a user hitting the + button or tapping on a cell. If they want a new present, then we set ourselves as the delegate (to get the addChristmasItemToList delegate callback). If it’s a details view they want, then we pass forward the details of the present.
Make one last trip to the MainStoryboard.storyboard file, and update the “Class” property on the Christmas Keeper Table to this class (ChristmasListTableViewController).
Take a deep breath. And build and run.
If everything went as planned, then: Our app. Is. Done!
Time to party like it’s the end of the year!
Where To Go From Here?
Here is an example project with all of the code from this tutorial.
Update 4/6/13: Here’s an updated version from Blake Loizides – modified to remove deprecation warnings in iOS 6.
Congratulations, you’ve covered the basics of security in iOS 5 and are ready to start using it in your own app – no more excuses! Hopefully this tutorial has gotten the point across that security is necessary but shouldn’t be intrusive (I’ve tried to sneak security features past you and then talk about them afterwards to demonstrate my point).
We’ve learned A LOT about security here – the iOS Keychain, Data Protection APIs, hashing and cryptography algorithms, and the new Secure Alert Views! Give yourself a pat on the back – it was a journey.
I hope you enjoyed this tutorial! As always, if you have any questions or comments on this tutorial or security in general, please join the discussion below!
This is a post by Chris Lowe, an iOS application developer and aspiring game developer living in San Antonio, Texas.