part I Developing a Professional UI COPYRIGHTED MATERIAL

28
PART I Developing a Professional UI CHAPTER 1: Creating a Personal Library CHAPTER 2: Advancing with Tableviews CHAPTER 3: Advancing with Map Kit CHAPTER 4: Understanding Action Views and Alerts CHAPTER 5: Internationalization: Building Apps for the World CHAPTER 6: Using Multimedia COPYRIGHTED MATERIAL

Transcript of part I Developing a Professional UI COPYRIGHTED MATERIAL

Page 1: part I Developing a Professional UI COPYRIGHTED MATERIAL

c1.indd 06-04-2013 05:31 PM

part IDeveloping a Professional UI

▶ ChaPter 1: Creating a Personal Library

▶ ChaPter 2: Advancing with Tableviews

▶ ChaPter 3: Advancing with Map Kit

▶ ChaPter 4: Understanding Action Views and Alerts

▶ ChaPter 5: Internationalization: Building Apps for the World

▶ ChaPter 6: Using Multimedia

COPYRIG

HTED M

ATERIAL

Page 2: part I Developing a Professional UI COPYRIGHTED MATERIAL

c1.indd 06-04-2013 05:31 PM

Page 3: part I Developing a Professional UI COPYRIGHTED MATERIAL

c1.indd 06-04-2013 05:31 PM

Creating a Personal LibraryWhat’S IN thIS ChaPter?

➤➤ Creating registering and login logic

➤➤ Configuring application settings

➤➤ Storing settings and securing password

➤➤ Handling crashes in your application

Wrox.Com CoDe DoWNloaDS for thIS ChaPter

The wrox.com code downloads for this chapter are found at www.wrox.com/go/proiosprog on the Download Code tab. The code is in the Chapter 1 download and individually named according to the names throughout the chapter.

In this chapter you learn how to develop a Personal Library, which is a series of classes and techniques you can use to give you a head start on your projects. Creating a Personal Library will save you time during future projects. The Personal Library you will create during this chapter will teach you how to implement the logic for user registration, user login, and secur-ing password storage.

If you’ve developed software before, you’ve likely noticed a lot of repetitive tasks in each application you’ve worked on.

This is no different when developing applications for iOS, and for that reason Xcode, the Apple Developer Platform, comes with different so-called project templates. The problem with these project templates is that they provide you only with some main object containers and don’t provide out-of-the-box configurable features that you require in each application. In this chapter you develop your own configurable Personal Library that you can use for all your sub-sequent applications. Throughout the different chapters in this book, you extend the Personal Library with additional functionalities.

1

Page 4: part I Developing a Professional UI COPYRIGHTED MATERIAL

4 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

CreatINg YoUr PerSoNal lIbrarY

The Personal Library you are about to create is basically a project skeleton containing differ-ent classes and functionalities to implement repetitive tasks in a flexible and organized way. Functionalities in the Personal Library are:

➤➤ Managing configuration settings

➤➤ Creating a user registration process with a UIViewController

➤➤ Implementing a user login process with a UIViewController

➤➤ Storing user values and secured password storage in a Keychain

In your future development projects, you can just drag and drop the files of the Personal Library you require.

Understanding Project basicsWhen you develop iOS applications, you have to make some basic choices when you create a new project. Two important choices are:

➤➤ Whether to use Automatic Reference Counting (ARC)

➤➤ Whether to use Interface Builder for the UI composition, or to code the UI components in your controller classes

Automatic Reference CountingARC is a technology introduced in iOS 5.x and higher that automatically manages your memory allocations. As the name suggests, it counts the references to objects and retains or releases objects automatically when required. The main advantages of using ARC are:

➤➤ No need to send release or autorelease messages to your objects

➤➤ Less chance for memory leaks in case you forget to release objects

➤➤ Less code to write because you can skip the release for your objects and also skip the dealloc method in most cases

The only reason for keeping a dealloc method around is when you need to free resources that don’t fall under the ARC umbrella, such as:

➤➤ Calling CFRelease on Core Foundation objects

➤➤ Calling free() on memory you allocated with malloc()

➤➤ Invalidating a timer

When you are using source code and/or libraries from other parties, you must keep in mind that not all of them have been updated to support ARC. If you need to work with a class that doesn’t support ARC and you still want to use ARC in your project, you can set a compiler flag for the file in ques-tion and assign it the value -fno -objc -arc, as shown in Figure 1-1.

Page 5: part I Developing a Professional UI COPYRIGHTED MATERIAL

Creating Your Personal Library ❘ 5

c1.indd 06-04-2013 05:31 PM

fIgUre 1-1

Interface BuilderBecause you are an experienced programmer, you know that Interface Builder is a part of Xcode that enables you to create and configure the UI elements of your project. Personally, I don’t like working with Interface Builder because I like to have complete control over all aspects of the UI by means of code. The side effect, however, is that a large amount of my code is UI-related code. To keep the source code listings focused on the subject in most of the examples, Interface Builder is used to create the user interface and the Assistant Editor is used to create the IBAction and IBOutlets.

Starting a New ProjectStart Xcode, create a new project using the Single View Application Project template, and name it MyPersonalLibrary using the configuration settings as shown in Figure 1-2.

fIgUre 1-2

Page 6: part I Developing a Professional UI COPYRIGHTED MATERIAL

6 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

The Class Prefix field will help you to follow Apple’s coding guidelines because each class within your project needs to have a unique name. When you create the project you can use this field to define your default class prefix, which will be used when you are creating a new class. Although Apple’s guide-lines advise using a three-character prefix, I always use a two-character prefix—the abbreviation of my company name, YourDeveloper—so I choose YD as a default prefix for my classes.

When you click the Next button, you will be asked to select a file location to store the project, and at the bottom of the screen you will see a checkbox that says Create Local git Repository for This Project, as shown in Figure 1-3. Check the option to create the local git repository and click the Create button to create the project with the chosen configuration.

fIgUre 1-3

USINg a loCal gIt rePoSItorY

When you select the option to create a local git repository, Xcode creates this reposi-tory for you automatically, which enables you to use version control on your project. With version control, Xcode keeps track of changes in your source code, enabling you to revert to a version, commit changes, compare versions, and so on. If you are using a server based subversion system (SVN) to manage your source control you don’t require a local git repository since you will be using an SVN client or SVN commands to man-age your sources. If you don’t use a server based subversion system I strongly recom-mend to check the Create Local git Repository option when creating a new project.

After the project is created, you’ll see two classes in the project: the YDAppDelegate class and the YDViewController class. Both classes are created by default by Xcode. You also see a YDViewController.xib file, which is an Interface Builder file.

Configuring Your ProjectIt’s good practice to use groups in your project structure to organize different elements like images, sounds, classes, views, helpers, and so on, so start by creating some groups under the MyPersonalLibrary node. Create the following groups:

➤➤ Externals

➤➤ Categories

➤➤ CrashHandler

➤➤ Helpers

Page 7: part I Developing a Professional UI COPYRIGHTED MATERIAL

Creating Your Personal Library ❘ 7

c1.indd 06-04-2013 05:31 PM

➤➤ Definitions

➤➤ Images

➤➤ VC

Because a group is only a container and not a physical folder on your filesystem, you should also cre-ate these folders on the filesystem under your project root folder.

Because there will be a lot of repetitive tasks in all the applications you develop, and you still need the flexibility to switch functionalities on and off without recoding and copying and pasting parts of code, create a configuration file as follows:

In the Project Explorer, navigate to the Definitions group and select New File from the context menu. Create a C header file called YDConstants.h as shown in Figures 1-4 and 1-5.

fIgUre 1-4

fIgUre 1-5

Page 8: part I Developing a Professional UI COPYRIGHTED MATERIAL

8 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

Defining ConstantsThe Personal Library you are developing contains functionality for registration of a user, login of a user, and a settings controller. In addition to that you’ll be implementing a crash handler.

Implement the code as shown in Listing 1-1 in your YDConstants.h file.

lIStINg 1-1: Chapter1/MyPersonalLibrary/YDConstants.h

#import <Foundation/Foundation.h>

// My application switches#define bYDActivateGPSOnStartUp YES#define bYDRegistrationRequired YES#define bYDLoginRequired NO#define bYDShowLoginAfterRegistration YES#define bYDInstallCrashHandler YES//keys that are used to store data#define bYDRegistered @"bYDRegistered"#define bYDAuthenticated @"bYDAuthenticated"#define bYDFirstLaunch @"bYDFirstLaunch"#define bYDVibrate @"bYDVibrate"

Using the ConfigurationYou can store settings using the NSUserDefaults class. Because you can store or change individual settings in many different places in your application, it’s important to first synchronize the object to make sure modifications are presented with the correct value.

Next, create a helper class that will give you some static methods to read and write values using the NSUserDefaults class.

In the Project Explorer, navigate to the Helpers group and select File ➪ New from the context menu. Create a new Objective-C class named YDConfigurationHelper that inherits from NSObject, as shown in Listing 1-2.

lIStINg 1-2: Chapter1/MyPersonalLibrary/YDConfigurationHelper.h

#import <Foundation/Foundation.h>

@interface YDConfigurationHelper : NSObject

+(void)setApplicationStartupDefaults;

+(BOOL)getBoolValueForConfigurationKey:(NSString *)_objectkey;

+(NSString *)getStringValueForConfigurationKey:(NSString *)_objectkey;

+(void)setBoolValueForConfigurationKey:(NSString *) _objectkey withValue:(BOOL)_boolvalue;

Page 9: part I Developing a Professional UI COPYRIGHTED MATERIAL

Creating Your Personal Library ❘ 9

c1.indd 06-04-2013 05:31 PM

+(void)setStringValueForConfigurationKey:(NSString *) _objectkey withValue:(NSString *)_value;

@end

The YDConfigurationHelper class in Listing 1-3 contains a few static methods to help you access the NSUserDefaults object. It contains get and set methods for NSString values and BOOL values to make life a lot easier.

lIStINg 1-3: Chapter1/MyPersonalLibrary/YDConfigurationHelper.m

#import "YDConfigurationHelper.h"

@implementation YDConfigurationHelper+(void)setApplicationStartupDefaults{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults synchronize]; [defaults setBool:NO forKey:bYDFirstLaunch]; [defaults setBool:NO forKey:bYDAuthenticated]; [defaults synchronize];}

+(BOOL)getBoolValueForConfigurationKey:(NSString *)_objectkey{ //create an instance of NSUserDefaults NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults synchronize]; //let's make sure the object is synchronized return [defaults boolForKey:_objectkey];}

+(NSString *)getStringValueForConfigurationKey:(NSString *)_objectkey{ //create an instance of NSUserDefaults NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults synchronize]; //let's make sure the object is synchronized if ([defaults stringForKey:_objectkey] == nil ) { //I don't want a (null) returned return @""; } else { return [defaults stringForKey:_objectkey]; }}+(void)setBoolValueForConfigurationKey:(NSString *) _objectkey withValue:(BOOL)_boolvalue{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults synchronize]; //let's make sure the object is synchronized

continues

Page 10: part I Developing a Professional UI COPYRIGHTED MATERIAL

10 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

[defaults setBool:_boolvalue forKey:_objectkey]; [defaults synchronize];//make sure you're synchronized again}

+(void)setStringValueForConfigurationKey:(NSString *) _objectkey withValue:(NSString *)_value{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults synchronize]; //let's make sure the object is synchronized [defaults setValue:_value forKey:_objectkey]; [defaults synchronize];//make sure you're synchronized again}

@end

The getStringValueForConfigurationKey: method is testing for a null value and returns an empty NSString instead of the null value. The reason is that in case you want to retrieve a value and assign it to the text property of a UILabel, you don’t want to display (null).

Importing the header fileAs you know, when you want to use a header definition file in your application you need to import it using the #import "YDConstants.h" statement.

This is not really convenient to repeat in each of your subsequent classes and ViewControllers. Each application, however, also has a precompiled header file that is applicable for the complete application. You can find this under the Supporting Files group in the Project Explorer; it is called MyPersonalLibrary-Prefix.pch.

If you import a header file here, it’s globally available. Add the import statements here for the YDConstants and YDConfigurationHelper header files, as shown in Listing 1-4.

lIStINg 1-4: Chapter1/MyPersonalLibrary/MyPersonalLibrary-Prefix.pch

#import <Availability.h>#ifdef __OBJC__ #import "YDConstants.h" #import"YDConfigurationHelper.h" #import <UIKit/UIKit.h> #import <Foundation/Foundation.h>#endif

In the next section, you create a login, a registration, and a setting ViewController.

lIStINg 1-3 (continued)

Page 11: part I Developing a Professional UI COPYRIGHTED MATERIAL

Registration—Login ❘ 11

c1.indd 06-04-2013 05:31 PM

regIStratIoN—logIN

Many applications require the user to register and log in, or log in only. If the user management is handled externally, you can ask a user to register in different ways. You can either ask for creden-tials like an e-mail address or a username and a password, or—what you see a lot nowadays—you can ask the user to register via Facebook.

Facebook registration is covered in depth in Chapter 14, “Social Media Integration,” and in that chapter, you will extend your Personal Library.

Introducing the iOS Keychain ServicesThe iOS Keychain Services provide you with a secure storage solution for passwords, keys, certifi-cates, notes, and custom data on the user’s device.

In your Personal Library you’ll be using the iOS Keychain Services to store the password the user enters during the registration process you’ll develop later.

To make interaction with the iOS Keychain Services more accessible, Apple has released a KeyChainItemWrapper class that will provide you with a higher-level API to work with the keychain.

You can download a sample project including the KeyChainItemWrapper class that you need in your Personal Library from http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html.

Download the sample project from the URL and navigate to its files using Finder. In your Xcode project use the Project Explorer to navigate to the Externals group, select New Group from the con-text menu, and name it KeyChain. Create a folder named keychain under the externals folder on your filesystem. Copy the KeychainItemWrapper.h and KeychainItemWrapper.m files from the download folder to your projects folder by using drag-and-drop. Xcode will prompt you to choose the options for adding these files. Make the same choices as shown in Figure 1-6.

fIgUre 1-6

Page 12: part I Developing a Professional UI COPYRIGHTED MATERIAL

12 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

This KeychainItemWrapper class has not been developed for ARC, and for that reason you must set the –fno –objc –arc compiler flag for the KeyChainItemWrapper.m file as already shown in Figure 1-1.

For more information about KeyChain Services programming, please visit https://developer .apple.com/library/ios/#documentation/security/Conceptual/keychainServConcepts/

01introduction/introduction.html.

To be able to work with the Keychain Services using the KeychainItemWrapper class, you also need to add a reference to the Security.framework.

Creating registration logicYour user registration screen may look different in each application because the design of each appli-cation will be different; nevertheless, you can standardize a part of the registration logic because you’ll always have to follow certain steps:

➤➤ Verify if the user has registered before so you don’t need to present the registration view again.

➤➤ If the user has registered before, define what subsequent processes are required; for exam-ple, present a login view or load data from a web service.

You already have defined a constant called bYDRegistrationRequired in the YDConstants header file, which you can use in your application delegate to check if you need to present a ViewController to capture the registration credentials.

Using the Project Explorer, navigate to the VC group and select New File from the context menu to create a UIViewController subclass called YDRegistrationViewController as shown in Figure 1-7.

fIgUre 1-7

In this YDRegistrationViewController class, you need to set up a delegate that will notify the delegate ancestor with the result of the registration process. Use Interface Builder and the Assistant Editor to create a user interface with a UILabel, two UITextFields, and two UIButton objects. The YDRegistrationViewController.xib file will look like Figure 1-8.

Page 13: part I Developing a Professional UI COPYRIGHTED MATERIAL

Registration—Login ❘ 13

c1.indd 06-04-2013 05:31 PM

fIgUre 1-8

The YDRegistrationViewController.h code is shown in Listing 1-5.

lIStINg 1-5: Chapter1/MyPersonalLibrary/YDRegistrationViewController.h

#import <UIKit/UIKit.h>@protocol YDRegistrationViewControllerDelegate <NSObject>

-(void)registeredWithSuccess;-(void)registeredWithError;-(void)cancelRegistration;@end

@interface YDRegistrationViewController : UIViewController

@property (nonatomic, assign) id<YDRegistrationViewControllerDelegate> delegate;@property (weak, nonatomic) IBOutlet UITextField *nameField;@property (weak, nonatomic) IBOutlet UITextField *passwordField;

- (IBAction)registerUser:(UIButton *)sender;- (IBAction)cancelRegistration:(UIButton *)sender;

@end

In YDRegistrationViewController.m, implement the registerUser: and cancelRegistration: methods as shown in Listing 1-6.

The registerUser: method first hashes the entered password into an MD5 string and then stores it in the keychain.

Page 14: part I Developing a Professional UI COPYRIGHTED MATERIAL

14 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

lIStINg 1-6: Chapter1/MyPersonalLibrary/YDRegistrationViewController.m

#import "YDRegistrationViewController.h"#import "NSString+MD5.h"#import "KeychainItemWrapper.h"@interface YDRegistrationViewController ()

@end

@implementation YDRegistrationViewController@synthesize delegate;- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self;}

- (void)viewDidLoad{ [super viewDidLoad]; // Do any additional setup after loading the view from its nib.}

- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}

- (IBAction)registerUser:(UIButton *)sender{ if (([self.nameField.text length]== 0 ) || ([self.passwordField.text length] == 0)) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Error" message:@"Both fields are mandatory" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [alert show]; } else { KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"YDAPPNAME" accessGroup:nil]; [keychain setObject:self.nameField.text forKey:(__bridge id)kSecAttrAccount]; [keychain setObject:[self.passwordField.text MD5] forKey:(__bridge id)kSecValueData]; //reading back a value from the keychain for comparison //get username [keychain objectForKey:(__bridge id)kSecAttrAccount]); //get password [keychain objectForKey:(__bridge id)kSecValueData]);

Page 15: part I Developing a Professional UI COPYRIGHTED MATERIAL

Registration—Login ❘ 15

c1.indd 06-04-2013 05:31 PM

[YDConfigurationHelper setBoolValueForConfigurationKey: bYDRegistered withValue:YES]; [self.delegate registeredWithSuccess]; //or //[self.delegate registeredWithError]; }}

- (IBAction)cancelRegistration:(UIButton *)sender{ [self.delegate cancelRegistration];}@end

Initializing DataEach application requires some default settings that are applicable during the launch of the applica-tion. For that reason, in this section you learn how to initialize application defaults.

Initializing application DefaultsSo far you’ve been working on creating a Personal Library that contains reusable and configurable code so you don’t have to write the same code in each of your applications. You’ve been using the NSUserDefaults class via the YDConfigurationHelper class you defined to store settings and results of operations.

The first time your application launches, none of these values are set. To support the initialization of your defaults, the YDConfigurationHelper class has a static method called setApplication-StartupDefaults. In the implementation, you set the initial value of each property to control your application’s logic at the first launch of an application, as in this example:

+(void)setApplicationStartupDefaults{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults synchronize]; [defaults setBool:NO forKey:bYDFirstLaunch]; [defaults setBool:NO forKey:bYDAuthenticated]; [defaults synchronize];}

You also may want to delete keychain items if they are left over from a previous install. This is also an ideal place if you need to create, for example, some special directories outside the bundle, so dur-ing further processing in your application you don’t have to test each time if a directory exists.

The purpose of the bYDFirstLaunch key is to support the fact that this initialization code is being called only once.

The following code snippet explains how you should call this method in the YDAppDelegate application: didFinishWithLaunchingOptions: method. It tests if the stored value returns YES, and in that case it calls setApplicationStartupDefaults, which sets the value to NO.

if (![YDConfigurationHelper getBoolValueForConfigurationKey:bYDFirstLaunch]) [YDConfigurationHelper setApplicationStartupDefaults];

Page 16: part I Developing a Professional UI COPYRIGHTED MATERIAL

16 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

Creating login logicIt is nice that a user can now register, but you also need to support the fact that a registered user can log in.

As a first step, create a new ViewController that serves as the ViewController for entering a user-name and a password. Create a new YDLoginViewController that inherits from UIViewController and code it like in Listing 1-7. Like in the YDRegistrationViewController, you define a protocol that can be used by your delegate so your delegate object will be notified with the result of the login process. Three methods are called depending on the result of the login process.

lIStINg 1-7: Chapter1/MyPersonalLibrary/YDLoginViewController.h

#import <UIKit/UIKit.h>@protocol YDLoginViewControllerDelegate <NSObject>

-(void)loginWithSuccess;-(void)loginWithError;-(void)loginCancelled;@end

@interface YDLoginViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITextField *nameField;@property (weak, nonatomic) IBOutlet UITextField *passwordField;

@property (nonatomic, assign) id<YDLoginViewControllerDelegate> delegate;

- (IBAction)loginUser:(UIButton *)sender;- (IBAction)cancelLogin:(UIButton *)sender;

@end

The implementation of the YDLoginViewController is shown in Listing 1-8.

lIStINg 1-8: Chapter1/MyPersonalLibrary/YDLoginViewController.m

#import "YDLoginViewController.h"#import "YDLoginViewController.h"#import "NSString+MD5.h"#import "KeychainItemWrapper.h"@interface YDLoginViewController ()

@end

@implementation YDLoginViewController@synthesize delegate;- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

Page 17: part I Developing a Professional UI COPYRIGHTED MATERIAL

Registration—Login ❘ 17

c1.indd 06-04-2013 05:31 PM

if (self) { // Custom initialization } return self;}

- (void)viewDidLoad{ [super viewDidLoad]; // Do any additional setup after loading the view from its nib.}

- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}

- (IBAction)loginUser:(UIButton *)sender{ if (([self.nameField.text length]== 0 ) || ([self.passwordField.text length] == 0)) { [self showErrorWithMessage:@"Both fields are mandatory!"]; } else { KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"YDAPPNAME" accessGroup:nil]; if ([self.nameField.text isEqualToString: [keychain objectForKey:(__bridge id)kSecAttrAccount]]) { if ([[self.passwordField.text MD5] isEqualToString:[keychain objectForKey: (__bridge id)kSecValueData]]) { [self.delegate loginWithSuccess]; } else [self showErrorWithMessage:@"Password not correct."]; } else [self showErrorWithMessage:@"Name not correct."]; }

}

- (IBAction)cancelLogin:(UIButton *)sender{ [self.delegate loginCancelled];}-(void)showErrorWithMessage:(NSString *)msg{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"

continues

Page 18: part I Developing a Professional UI COPYRIGHTED MATERIAL

18 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

message:msg delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [alert show];}@end

In the loginUser: method you see that the first check performed is if both fields have been entered with data. Next, the logic checks if the username is stored and, if so, if the password stored in the keychain is equal to the one that has been entered.

The YDLoginViewController.xib file should look something like Figure 1-9.

fIgUre 1-9

Securing PasswordsIn your YDRegistrationViewController, you accept a value for the username and for the pass-word and store it in the keychain of the device. Many developers still store the username and pass-word using the NSUserDefaults class, which is not a secure solution.

This is not a very secure solution because the password is stored in plaintext, and the NSUserDefaults object is not 100 percent safe and can be hacked.

To make your Personal Library more secure, implement a category of NSString that creates an MD5 hashed value of the password and store it in the keychain; the only safe place on the device to store critical data.

In the Project Explorer, navigate to the Categories group and select New File from the context menu. Select Objective-C Category from the template list as shown in Figure 1-10.

lIStINg 1-8 (continued)

Page 19: part I Developing a Professional UI COPYRIGHTED MATERIAL

Registration—Login ❘ 19

c1.indd 06-04-2013 05:31 PM

fIgUre 1-10

Click the Next button, and in the next screen enter MD5 as the name of the category you are creat-ing. From the Category On drop-down list, select NSString as shown in Figure 1-11.

fIgUre 1-11

The header file is shown in Listing 1-9.

lIStINg 1-9: Chapter1/MyPersonalLibrary/NSString+MD5 header

#import <Foundation/Foundation.h>

@interface NSString (MD5)- (NSString *)MD5;@end

Page 20: part I Developing a Professional UI COPYRIGHTED MATERIAL

20 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

The implementation of your category is shown in Listing 1-10 and the explanation is in the code.

lIStINg 1-10: Chapter1/MyPersonalLibrary /NSString+MD5 implementation

#import "NSString+MD5.h"#import <CommonCrypto/CommonDigest.h>@implementation NSString (MD5)- (NSString*)MD5{ // Create pointer to the string as UTF8 const char *ptr = [self UTF8String]; // Create byte array of unsigned chars unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH]; // Create 16 bytes MD5 hash value, store in buffer CC_MD5(ptr, strlen(ptr), md5Buffer); // Convert unsigned char buffer to NSString of hex values NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) [output appendFormat:@"%02x",md5Buffer[i]]; return output;}@end

In this category you import the <CommonCrypto/CommonDigest.h> header file. To do this, you must add the Security.framework to your project if you haven’t already done that when adding the KeychainItemWrapper class.

Storing the Password in a KeychainApple has developed a keychain wrapper class that makes it easy for you to work with the keychain.

The KeychainItemWrapper classes are part of the download of this book and are taken directly from Apple’s developer portal. This KeychainItemWrapper class has not been devel-oped for ARC, and for that reason you must set the –fno –objc –arc compiler flag for the KeyChainItemWrapper.m file as shown earlier in Figure 1-1.

CraSh maNagemeNt

Despite all your programming efforts and extensive test processes, your application still might crash. There can be many different reasons for an application to crash, and sometimes it may not even relate to your application at all. In this section you learn about the kinds of crashes that hap-pen and how to implement a crash handler that shows a UIAlertView with a standard message just before the app crashes, so your user knows the application is crashing and might be less irritated.

Page 21: part I Developing a Professional UI COPYRIGHTED MATERIAL

Crash Management ❘ 21

c1.indd 06-04-2013 05:31 PM

The two main reasons for a crash are:

➤➤ Memory leaks

➤➤ Over-releasing objects

Memory leaks are most often caused by not releasing your objects. UIImageView, UIImage, and UIWebView are consuming a lot of memory; they definitely need to be released at the right time.

Over-releasing happens when you try to access an object that is already released.

USINg INStrUmeNtS

Instruments is a performance, analysis, and testing tool for dynamically tracing and profiling iOS and OS X code. It’s important to have a good understanding of Instruments and how it will help you in improving the quality of your code, by identifying memory leaks, giving you insight into memory consumption, and many other performance-relevant measures. Please visit http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/

InstrumentsUserGuide/Introduction/Introduction.html for the complete documentation on Instruments.

You can use Instruments and profile your application to identify memory leaks. You should use the Zombie instrument to identify access to an object that is already released (also known as a zombie).

Understanding CrashesAt run time, the application’s signal handling can, by default, launch six different standard signals in case an application crashes. These signals are:

➤➤ SIGABRT: An abnormal termination

➤➤ SIGFPE: A floating-point exception

➤➤ SIGILL: An invalid instruction

➤➤ SIGINT: An interactive attention request sent to the application

➤➤ SIGSEGV: Access to an invalid memory address

➤➤ SIGTERM: Termination request sent to the application

In the next section, you build a global exception handler that captures the signal and presents a UIAlertView to notify the user.

Implementing a Crash handlerCreate a new class called YDCrashHandler as shown in Listing 1-11.

Page 22: part I Developing a Professional UI COPYRIGHTED MATERIAL

22 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

lIStINg 1-11: Chapter1/MyPersonalLibrary/YDCrashHandler.h

#import <Foundation/Foundation.h>

@interface YDCrashHandler : NSObject{ BOOL dismissed;}void InstallCrashExceptionHandler();@end

The implementation of the class is shown in Listing 1-12.

The class installs an NSSetUncaughtExceptionHandler for each of the available signals. If an exception occurs, the handler is invoked and accesses all modes in the current RunLoop. The pro-cess is killed and the back trace is read into an NSDictionary and passed to the handleException method that presents a UIAlertView.

lIStINg 1-12: Chapter1/MyPersonalLibrary/YDCrashHandler.m

#import "YDCrashHandler.h"#include <libkern/OSAtomic.h>#include <execinfo.h>

NSString * const YDCrashHandlerSignalExceptionName = @"YDCrashHandlerSignalExceptionName";NSString * const YDCrashHandlerSignalKey = @"YDCrashHandlerSignalKey";NSString * const YDCrashHandlerAddressesKey = @"YDCrashHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount = 0;const int32_t UncaughtExceptionMaximum = 10;

const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;@implementation YDCrashHandler+ (NSArray *)backtrace{ void* callstack[128]; int frames = backtrace(callstack, 128); char **strs = backtrace_symbols(callstack, frames); int i; NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames]; for ( i = UncaughtExceptionHandlerSkipAddressCount; i < UncaughtExceptionHandlerSkipAddressCount + UncaughtExceptionHandlerReportAddressCount; i++) { [backtrace addObject:[NSString stringWithUTF8String:strs[i]]]; } free(strs);

Page 23: part I Developing a Professional UI COPYRIGHTED MATERIAL

Crash Management ❘ 23

c1.indd 06-04-2013 05:31 PM

return backtrace;}

- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex{ //if (anIndex == 0) //{ dismissed = YES; //}}

- (void)handleException:(NSException *)exception{ UIAlertView *thisAlert = [[UIAlertView alloc] initWithTitle:@"Sorry" message:@"An unexpected event happened causing the application to shutdown." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [thisAlert show]; CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); while (!dismissed) { for (NSString *mode in (NSArray *)CFBridgingRelease(allModes)) { CFRunLoopRunInMode((CFStringRef)CFBridgingRetain(mode), 0.001, false); } } CFRelease(allModes); NSSetUncaughtExceptionHandler(NULL); signal(SIGABRT, SIG_DFL); signal(SIGILL, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGBUS, SIG_DFL); signal(SIGPIPE, SIG_DFL); if ([[exception name] isEqual:YDCrashHandlerSignalExceptionName]) { kill(getpid(), [[[exception userInfo] objectForKey:YDCrashHandlerSignalKey] intValue]); } else { [exception raise];

continues

Page 24: part I Developing a Professional UI COPYRIGHTED MATERIAL

24 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

}}

@end

void HandleException(NSException *exception){ int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); if (exceptionCount > UncaughtExceptionMaximum) { return; } NSArray *callStack = [YDCrashHandler backtrace]; NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]]; [userInfo setObject:callStack forKey:YDCrashHandlerAddressesKey]; [[[YDCrashHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject: [NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo] waitUntilDone:YES];}

void SignalHandler(int signal){ int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); if (exceptionCount > UncaughtExceptionMaximum) { return; } NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:YDCrashHandlerSignalKey]; NSArray *callStack = [YDCrashHandler backtrace]; [userInfo setObject:callStack forKey:YDCrashHandlerAddressesKey]; [[[YDCrashHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject: [NSException

lIStINg 1-12 (continued)

Page 25: part I Developing a Professional UI COPYRIGHTED MATERIAL

Crash Management ❘ 25

c1.indd 06-04-2013 05:31 PM

exceptionWithName:YDCrashHandlerSignalExceptionName reason: [NSString stringWithFormat:@"Signal %d was raised.", signal] userInfo: [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:YDCrashHandlerSignalKey]] waitUntilDone:YES];}

void InstallCrashExceptionHandler(){ NSSetUncaughtExceptionHandler(&HandleException); signal(SIGABRT, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGBUS, SignalHandler); signal(SIGPIPE, SignalHandler);}

Your Personal Library is now ready to be used. Open your YDAppDelegate.h file and implement the code as shown in Listing 1-13.

lIStINg 1-13: Chapter1/MyPersonalLibrary/YDAppDelegate.h

#import <UIKit/UIKit.h>#import "YDRegistrationViewController.h"#import "YDLoginViewController.h"

@class YDViewController;@interface YDAppDelegate : UIResponder <UIApplicationDelegate, YDLoginViewControllerDelegate, YDRegistrationViewControllerDelegate>

@property (strong, nonatomic) UIWindow *window;@property (strong, nonatomic) YDViewController *viewController;@property (strong, nonatomic) YDLoginViewController *loginVC;@property (strong, nonatomic) YDRegistrationViewController *registrationVC;

@end

Open your YDAppDelegate.m implementation file and write the code as shown in Listing 1-14.

The application: didFinishLaunchingWithOptions: method first checks if the YDCrashHandler needs to be installed. Next, it checks if the application runs for the first time, and if so, it sets the application startup defaults using the YDConfigurationHelper class.

Next, it follows a logic that tests whether or not registration is required. If the previous test returns true, a second test is performed to see if a user registration has been executed before. If registration

Page 26: part I Developing a Professional UI COPYRIGHTED MATERIAL

26 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

needs to be done, the YDRegistrationViewController is presented and the delegate implemen-tation checks if the YDLoginViewController needs to be presented. After the login process has completed with success, the YDMainViewController is presented. The complete implementation is shown in Listing 1-14.

lIStINg 1-14: Chapter1/MyPersonalLibrary/YDAppDelegate.m

#import "YDAppDelegate.h"

#import "YDViewController.h"#import "YDCrashHandler.h"@implementation YDAppDelegate

- (void)installYDCrashHandler{ InstallCrashExceptionHandler();}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions{ if (bYDInstallCrashHandler) { [self performSelector:@selector(installYDCrashHandler) withObject:nil afterDelay:0]; } self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

if (![YDConfigurationHelper getBoolValueForConfigurationKey:bYDFirstLaunch]) [YDConfigurationHelper setApplicationStartupDefaults]; if (bYDActivateGPSOnStartUp) { //Start your CLLocationManager here if you're application needs the GPS } if (bYDRegistrationRequired && ![YDConfigurationHelper getBoolValueForConfigurationKey:bYDRegistered]) { //Create an instance of your RegistrationViewcontroller self.registrationVC =[[YDRegistrationViewController alloc] init]; //Set the delegate self.registrationVC.delegate=self; self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.rootViewController = _registrationVC; self.window.backgroundColor = [UIColor clearColor]; [self.window makeKeyAndVisible]; }

Page 27: part I Developing a Professional UI COPYRIGHTED MATERIAL

Crash Management ❘ 27

c1.indd 06-04-2013 05:31 PM

else { // you arrive here if either the registration is not required or yet achieved if (bYDLoginRequired) { self.loginVC= [[YDLoginViewController alloc] init]; self.loginVC.delegate=self; self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.rootViewController = _loginVC; self.window.backgroundColor = [UIColor clearColor]; [self.window makeKeyAndVisible]; } else { self.viewController= [[YDViewController alloc] init]; self.window.rootViewController =self.viewController; [self.window makeKeyAndVisible]; } }

}#pragma Registration Delegates-(void)registeredWithError{ //called from RegistrationViewcontroller if registration failed}-(void)registeredWithSuccess{ //called from RegistrationViewcontroller if the registration with success // if (bYDShowLoginAfterRegistration) { self.loginVC = [[YDLoginViewController alloc] init]; self.loginVC.delegate=self; self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.rootViewController = self.loginVC; self.window.backgroundColor = [UIColor clearColor]; [self.window makeKeyAndVisible]; } else { self.viewController= [[YDViewController alloc] init]; self.window.rootViewController =self.viewController; [self.window makeKeyAndVisible]; }}-(void)cancelRegistration{ //called from RegistrationViewcontroller if cancel is pressed}

continues

Page 28: part I Developing a Professional UI COPYRIGHTED MATERIAL

28 ❘ ChaPter 1 Creating a Personal library

c1.indd 06-04-2013 05:31 PM

#pragma Login delegates-(void)loginWithSuccess{ //called when login with success self.viewController= [[YDViewController alloc] init]; self.window.rootViewController =self.viewController; [self.window makeKeyAndVisible];}-(void)loginWithError{ //called when login with error}-(void)loginCancelled{ //called when login is cancelled}

@end

SUmmarY

In this chapter you created a configurable Personal Library that you can use as a starting point for your applications. This Personal Library supports the following features:

➤➤ A single location for configuration settings

➤➤ A registration process with a ViewController

➤➤ A login process with a ViewController

➤➤ Storing values in user defaults

➤➤ Securing password storage by MD5 encryption and keychain storage

➤➤ A Settings ViewController that enables you to use checkboxes to retrieve and store applica-tion settings

➤➤ A crash handler to avoid unexpected crashes of your application

In the next chapter, you learn how to advance your applications using UITableView objects and cus-tomize them to your needs. After you understand the basics for working with UITableView objects, you develop a custom UITableView solution that results in a chat view controller, like the one used in iMessage. Finally, you develop a custom UITableView that supports drill-down functionality to improve the user’s experience.

lIStINg 1-14 (continued)