Cocoa and Objective-C Cookbook

By Jeff Hawkins
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. User Interface Components

About this book

Much of Cocoa is implemented in Objective-C, an object-oriented language that is designed to run at high speed. In order to build powerful Cocoa applications you need more than the basics. This cookbook will provide you with the recipes needed to add Core Animation, gestures, Key Value Coding, and QuickTime to your appilications.

The Cocoa and Objective-C Cookbook moves developers beyond the basics of developing with Apple's Cocoa framework. It will help you grasp advanced topics needed to build polished Cocoa applications on Mac OS X.

The cookbook provides a comprehensive overview of Cocoa's more popular UI components found in all Mac OS X applications. It has recipes for building custom views, adding support for gestures and working with keyboard and mouse events. There are recipes for using singleton, delegation, and factory design patterns in your own application's architecture. Alongside essential recipes for working with databases and debugging you will also find fun recipes covering animation and multimedia. The Cocoa and Objective-C Cookbook will quickly bring you up to speed with advanced technologies used to build complex applications for Mac OS X.

Publication date:
May 2011
Publisher
Packt
Pages
248
ISBN
9781849690386

 

Chapter 1. User Interface Components

In this chapter, we will cover:

  • Using a NSTableView

  • Using a NSOutlineView

  • Using NSSplitView

  • Using the WebView

  • Displaying a NSAlert

  • Formatting dates

  • Formatting numbers

  • Importing images

  • Saving preferences with NSUserDefaults

  • Retrieving preferences with NSUserDefaults

  • Adding a password to KeyChain

  • Retrieving a password from KeyChain

  • Accessing the Address Book

  • Adding an event to iCal

 

Introduction


When building applications in Cocoa, there are several user interface components and concepts that are key to all applications. In this chapter, we will cover some of Cocoas more advanced and most often used interface elements. The formatting of dates and numbers will also be covered. How to best use alerts and sheets when you need to display errors or warnings to the user will also be covered. We will look at how to quickly save defaults or user preferences for your application. We will also work with KeyChain to illustrate how to securely store and retrieve passwords. Lastly, we will cover working with the address book and calendar stores.

Note

Some recipes in this chapter require Mac OS X 10.6.

 

Using a NSTableView


The NSTableView is one of the most used Cocoa UI elements in Mac OS X. It is used everywhere from simple lists to complex interactive tables.

In this recipe, we will create a simple table view that will provide you with the basic information needed to implement and extend a table view in your application.

Getting ready

Start Xcode and choose New Project... from the File menu. Once the New Project dialog appears, choose the following options. Under Mac OS X in the left pane, select Application, then choose Cocoa Application and click on the Choose... button. Name your project TableView and save it:

How to do it...

  1. 1. Click on the TableViewAppDelegate.h file to open it so that we can modify the interface of the TableViewAppDelegate class.

  2. 2. First, lets add the NSTableViewDataSource protocol to the interface. This marks our class as implementing the methods required to be a data source for the table view:

    @interface TableViewAppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource>
    

    Note

    Downloading the example code

    You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

    1. 1. We need to add two variables to the interface directly below the NSWindow reference. The first is a NSTableView *tableView; and the second is NSMutableArray *tableData; The tableView is a reference to the NSTableView we are going to add via Interface Builder and the second will act as a data source and will hold the sample data for our table view. We need to add a property for the NSTableView and we need to use the special keyword IBOutlet which allows Interface Builder to see our reference:

      @property (assign) IBOutlet NSWindow *window;
      @property (retain) IBOutlet NSTableView *tableView;
      
    2. 2. From the project view in Xcode, click on the TableViewAppDelegate.m so we can modify the implementation of our class.

    3. 3. We need to synthesize our tableView variable, so below the synthesized window variable, add @synthesize tableView;.

    4. 4. Next we need to implement some sample data for our table view. We will do that with the tableData variable that we added earlier. In the applicationDidFinishLaunching: method of our class, we will add three rows of data:

      tableData = [[NSMutableArray alloc] init];
      // Row 1
      NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
      [dictionary setObject:@"Benjamin Franklin" forKey:@"name"];
      [dictionary setObject:@"1/17/1706" forKey:@"birthdate"];
      [tableData addObject:dictionary];
      // Row 2
      dictionary = [NSMutableDictionary dictionary];
      [dictionary setObject:@"Samuel Adams" forKey:@"name"];
      [dictionary setObject:@"9/27/1722" forKey:@"birthdate"];
      [tableData addObject:dictionary];
      // Row 3
      dictionary = [NSMutableDictionary dictionary];
      [dictionary setObject:@"Thomas Jefferson" forKey:@"name"];
      [dictionary setObject:@"4/13/1743" forKey:@"birthdate"];
      [tableData addObject:dictionary];
      [tableView reloadData];
      
    5. 5. There are two methods that we are required to implement as a data source for a table view. The first of those methods is numberOfRowsInTableView: which will tell the table view how many rows of data we need to display. The second method we need to implement is tableView:objectValueForTableColumn:row: This method will provide the data for the table view. We will pull our data from our data source based on the parameters passed to us in this method:

      - (NSInteger) numberOfRowsInTableView:(NSTableView *)table {
      return [tableData count];
      }
      - (id)tableView:(NSTableView *)table objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)rowIndex {
      NSDictionary *rowData = [tableData objectAtIndex:rowIndex];
      return [rowData valueForKey:[column identifier]];
      }
      
    6. 6. We are now ready to work with Interface Builder to lay out the table view. Double-click on the MainMenu.xib in Xcode's project view. This will open the user interface file in Interface Builder.

    7. 7. If the Library palette is not currently displayed in Interface Builder, choose Library from the Tools menu.

    8. 8. Drag a Table View from the Library to your window. You may want to adjust the table view to fully fill the window and automatically resize when the window resizes. This can be done from the Sizes tab of the Inspector. If the Inspector is not currently shown, you can display it from the Tools menu of Interface Builder.

    9. 9. Now we need to add some columns to our table view. Make sure you have selected the NSTableView, and then add a Name column and a Birth Date column to the table view. In order to do this, select the Inspector's Attributes tab and change the number of columns to two. By double-clicking on the first column, you can change the column's properties in the Inspector. Change the Title to Name and the identifier to name. Make the same changes to the second column. Its title should be Birth Date and its identifier should be birthdate.

    10. 10. In Interface Builder, we need to connect the table view to our references in the implementation of our class. In the main window of Interface Builder control, click on Table View App Delegate. The Outlets window will appear. Mouse over the hollow circle of the tableView Outlet, it should change to a plus inside the circle. Click and drag from the circle with the plus to the table view you dragged into the window earlier. You will see the Table View flash as an indicator that you have selected it. Release the mouse button and the connection will be complete:

    11. 11. We now need to tell the table view that our Table View App Delegate will be the data source for the table view. Control click on the table view in your layout. From the Outlets window, mouse over the hollow circle for data source outlet. Click and drag over to the Table View App Delegate in the main window. When the Table View App Delegate highlights, release the mouse button:

    12. 12. Back in Xcode, choose Build and Run to see the table view in action.

How it works...

Cocoa relies heavily on the delegation design pattern. Basically, your controller classes will act as the delegates for the Cocoa UI objects. In this example, our TableViewAppDelegate class is the delegate for the NSTableView. As the NSTableView goes through its redrawing process, it calls our class for the information it needs to draw the table, such as the number of rows and the data for each row and column. In our example, we used the NSMutableArray as a data source for the table view. The array contained NSDictionaries for each row, using a name and birth date for each column in the table view. You are not limited to using NSArrays for the data source. You may design your own custom class or data structure to contain your table view's data. In fact, most real world applications data is retrieved from a database or a file.

Next, we used Interface Builder to add the NSTableView to our application window. Interface Builder makes it easy to position UI elements and set their properties, something we would otherwise have to do via code. We also used Interface Builder to connect our NSTableView to its data source.

One last detail about the table view is the reloadData method. You will need to call this method when you change data in your data source. This will force the table view to redraw and in that process call your delegate methods and therefore update the table view with the latest changes in your data source.

There's more...

There are many other delegate methods that we can implement for the table view to handle such things as data being dropped on the table from a drag-and-drop operation or sorting the data in the table.

We can also provide the NSTableView with a custom view to fill the area where the horizontal and vertical scroll bars meet in the lower right-hand corner of the table view. This area is known as the corner view. By right-clicking on the table view within Interface Builder, we can assign our custom view.

Depending on your application design, it may be necessary to use a custom drawn cell in your table view. Cocoa makes this possible by allowing you to provide your own subclass of NSCell. To implement this you need to set your custom NSCell class on the table's column. You then implement the delegate method willDisplayCell: in your delegate's class. When the table view calls you during its drawing process, the cell parameter of willDisplayCell: will be an instance of your custom NSCell subclass. You can then set whatever custom properties your cell requires to draw itself.

See also

The sample code and project for this recipe is located under the TableView folder in the code bundled with this book.

 

Using a NSOutlineView


NSOutlineView's are the preferred method for displaying data in a hierarchal manner. In this recipe, we will create a simple outline view and describe the delegate methods you will need to implement in your controller class in order to display your data correctly.

Getting ready

In Xcode, choose New Project... from the File menu. As we have done in other recipes, choose Application from under the Mac OS X section and then choose Cocoa Application. Name the new project OutlineView and save.

How to do it...

  1. 1. Open the OutlineViewAppDelegate.h so that we can add the protocol, variables, and a property to our class interface.

  2. 2. Add the NSOutlineViewDelegate protocol to the class interface.

  3. 3. Next, we need to add our outline view variable reference. Below the NSWindow reference, add the following: NSOutlineView *outlineView;. We also need to add our data source variable: NSMutableDictionary *outlineViewData;.

  4. 4. We need to add a property for the outline view, so add: @property (retain) IBOutlet NSOutlineView *outlineView; below the property for the NSWindow.

  5. 5. Now, click on OutlineViewAppDelegate.m from the project view to open it.

  6. 6. Add @synthesize outlineView; below the line for the synthesized window.

  7. 7. We are now ready to work with Interface Builder to add the NSOutlineView to our applications window. Double-click on the MainMenu.xib in Xcode's project view. This will start Interface Builder and open our interface file.

  8. 8. Drag a Outline View from the Library to your window. You may want to adjust the outline view to fully fill the window and automatically resize when the window resizes.

  9. 9. Next, we need to connect the references, called outlets, in our code with the UI elements in Interface Builder. Control click on the Outline View App Delegate in the main window of Interface Builder. When the outlet's window appears, you will need to connect the outlineView outlet to the NSOutlineView that you dragged into the main window. Note that the NSOutlineView will flash when you have selected the outline view:

  10. 10. Control-click on the outline view you dragged into the window. Make a connection from the data source in the outlet window to the Outline View App Delegate in the main window:

  11. 11. We now need to add some sample data for our data source. Note that we are going to use an NSMutableDictionary as our data source and this will contain all the root items for the outline view. Each of the items under the root item will be an NSArray of items. In this sample, it will be the U.S. Area Codes for its parent state.

  12. 12. Back in Xcode, we need to create some sample data for our outline view. In the applicationDidFinishLaunching: method lets add the following:

    outlineViewData = [[NSMutableDictionary dictionary] retain];
    // Item 1
    NSArray *items = [NSArray arrayWithObjects:@"229", @"404", @"478", @"678", @"706", @"770", @"912", nil];
    [outlineViewData setObject:items forKey:@"Georgia"];
    // Item 2
    items = [NSArray arrayWithObjects:@"240", @"301", @"410", @"443", nil];
    [outlineViewData setObject:items forKey:@"Maryland"];
    // Item 3
    items = [NSArray arrayWithObjects:@"206", @"253", @"360", @"425", @"509", nil];
    [outlineViewData setObject:items forKey:@"Washington"];
    [outlineView reloadData];
    
  13. 13. Next, lets add the delegate methods that the outline view will call to populate the view. First, we need to implement outlineView:numberOfChildrenOfItem:. In this method, we need to return the number of child items for the passed item parameter. Note that if item is nil, we need to return the number of root items. In our example, this would be the number of entries in the outlineViewData dictionary. Since we are using NSArray's and NSDictionaries in our data source implementation, we can simply return the count of items since both array's and dictionaries implement the count method:

    - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
    if (item == nil) {
    return [outlineViewData count];
    }
    return [item count];
    }
    
  14. 14. The outline view needs to know which item from the data source to display for a particular index. We provide this information to the outline view by implementing the outlineView:child:ofItem: method. Since we are using arrays and dictionaries to hold data for the outline view, we use isKindOfClass: to determine if item is an array or dictionary. We can then determine how best to get to the data we need to return.

  15. 15. The next method our delegate needs to implement is outlineView:objectValueForTableColumn:byItem:. In this method, there are two types of objects that can represent the item parameter. Those types are NSString and NSArray. Note that these are the same types we use in our data source. Therefore, we need to use isKindOfClass: again to determine the type of item being passed to us. If the item is an NSString, we just return the item, otherwise we see if it is a array and if so, find the key in the outlineViewData dictionary that represents this array. We then return that key, or in our case the state's name.

  16. 16. The final delegate we will implement is the outlineView:isItemExpandable: method. We simply need to return whether or not the passed item is capable of being expanded. If the item is a dictionary or array that has an object count, then we return true.

  17. 17. Choose Build and Run from the toolbar in Xcode to test your outline view application.

How it works...

The outline view works by calling your delegate methods during the redrawing of the outline view. Some of the delegate methods that you implement will be called for each item in the outline view. This is important to remember since the type of the item parameter will be of the generic type id and you will have to determine what item really is using isKindOfClass: in order to return the correct information for the outline view.

Next, we used Interface Builder to add a NSOutlineView to our applications main window. Interface Builder also lets us set properties on the outline view to allow it to resize with the applications window at runtime.

We linked the outline view and data source references in our code with the outline view in Interface Builder so the OutlineViewAppDelegate class could act as the delegate and data source for the NSOutlineView.

As with the NSTableView, you can call the reloadData method to force a redraw of the outline view if the data in your data source changes.

There's more...

How you structure your data for the outline view's data source will make working with the outline view a bit easier. In our example, we used an NSMutableDictionary to hold all of the root items. We then used NSArrays to store all the items for each of the root items. This structure is simple, but may be added to if needed. We could add another NSDictionary in place of the NSArray's and we would have another level in our hierarchy.

Since NSOutlineView is a subclass of NSTableView, you can think of the outline view as expanding and collapsing the table view's rows. The outline view also can have additional columns to display related information about its items. For example, if your outline view was showing a folder listing, you might have a column to show the folder's size in bytes or the number of items in the folder.

See also

The sample code and project for this recipe is located under the OutlineView folder in the code bundled with this book.

 

Using NSSplitView


A split view is a great way to have multiple resizable views within your application. A split view works well when you need to have a list of items in one area of the split view, commonly the top view, and a detail view in the other. Apple's Mail.app uses this approach to display items from your inbox in the top of the split view and then the actual message in the bottom view.

Getting ready

Let's head back into Xcode and create a new Project from the File menu. Lets save this project with the name SplitView.

How to do it...

  1. 1. As we have done in the other recipes in this chapter, we need to open the SplitViewAppDelegate.h to add the delegate, in this case a NSSplitViewDelegate, and a variable to the class interface. Below the reference to NSWindow, add the following variable NSSplitView *splitView;.

  2. 2. Next, we need to add a property for the split view variable. Below the property for the NSWindow, add: @property (retain) IBOutlet NSSplitView *splitView;.

  3. 3. Now its time to add a NSSplitView to our application's window using Interface Builder. In the project window of Xcode, double-click on the MainMenu.xib file to open the interface file.

  4. 4. From the Library palette in Interface Builder, locate the Horizontal Split View and drag it to the application window. You may want to adjust the split view's properties so that it is the same size as the window and resizes with the application window. This can be accomplished from the Size tab in the Attributes Inspector.

    We now need to connect the split view to our reference in the code. Control click on the Split View App Delegate and from the outlets window, drag a connection from the splitView outlet to the split view in the applications window:

  5. 5. Back in Xcode, choose Build and Run from the toolbar. If all goes well, your application will run with a split view. Note that you can grab the divider and resize the split view.

How it works...

The split view is a pretty simple view in Cocoa and is really nothing more than a dynamic container for other Cocoa views or your own custom views.

There's more...

The NSSplitView class allows you to be notified via delegation when the user resizes the split view with the divider. As we have done in other recipes, you can use Interface Builder to connect the split view to your Split View App Delegate. After making this connection, you can implement one of the many delegate methods.

See also

The sample code and project for this recipe is located under the SplitView folder in the code bundled with this book.

 

Using the WebView


The WebView is used to embed web content in your application. Using a WebView, you can build advanced web browser functionality in your application.

Getting ready

In Xcode, lets create a new Cocoa Application project and save it as WebView.

How to do it...

  1. 1. The WebView is based on the WebKit framework, so we will need to include the WebKit.framework into our project. Right-click on Frameworks folder in the Project view of Xcode and choose Add, then choose Existing Frameworks... From the Frameworks window, choose WebKit.framework:

  2. 2. Open the WebViewAppDelegate.h file. We need to add the import for the WebView before we can use it. Below the #import <Cocoa/Cocoa.h>, add #import <WebKit/WebKit.h>

  3. 3. We also need to add a variable for our web view. Add WebView *webView; below the window variable.

  4. 4. Next, add a property for the webView variable: @property (retain) IBOutlet WebView *webView;

  5. 5. Open the WebViewAppDelegate.m file and add @synthesize webView; after @synthesize window.

  6. 6. Double-click on MainMenu.xib to open Interface Builder. Drag a Web View from the Library palette to the main window and adjust the size of the view to fit the window.

  7. 7. Next, we need to connect the outlet and delegate for the web view. Right-click on the Web View App Delegate and drag a connection from the webView outlet to the web view in the applications window. Next right-click on the web view in the applications window and drag a connection from frameLoadDelegate to the Web View App Delegate.

  8. 8. Next, lets add some code to display a web page in our web view. In the applicationDidFinishLaunching: method, let's add the following code:

    NSURL *url = [NSURL URLWithString:@"http://www.packtpub.com"];
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
    [[webView mainFrame] loadRequest:urlRequest];
    
  9. 9. Click the Build and Run toolbar button in Xcode and let's see our web view in action.

How it works...

The WebView works by handling all the details of parsing and displaying HTML, CSS, and JavaScript for you. Basically, all you have to manage is the main frame and the URLs to the web content you want to display. Since Cocoa's WebView is based on WebKit, you can provide similar functionality to what is available in the Safari web browser included in Mac OS X.

There's more...

By implementing just a few of WebView's delegate methods, our application can be notified when the title element has loaded and when the page has completed loading into the web view.

If you need to know the title element, implement the following delegate:

webView:didReceiveTitle:forFrame:. If you need to know when the page has finished loading, implement the webView:didFinishLoadForFrame: delegate method.

The sample code for this recipe has implemented both of these methods and displays an alert when we receive the notifications.

See also

The sample code and project for this recipe is located under the WebView folder in the code bundled with this book.

 

Displaying a NSAlert


At some point, while the user is interacting with your application, you will need to show them a message, warning, or even an error. The NSAlert class makes simple work of creating and displaying alerts. Cocoa provides simple methods for you to create alerts quickly and easily. Cocoa also provides two different ways to display modal dialogs. One is a dialog window and the other is a sheet that drops down from the parent window.

Getting ready

In Xcode, select the File menu and choose New Project... As we have done in the other recipes, we will create a new Cocoa Application. Name the project Alert and save. In this recipe, we are going to create a single button that will display several different types of alerts.

How to do it...

  1. 1. Open the AlertAppDelegate.h file and add a NSButton variable *showAlertsButton below the NSWindow variable.

  2. 2. Add a property for the button: @property (retain) IBOutlet NSButton *showAlertsButton;

  3. 3. Next, you will need to add a method signature for the button's action method. Add the - (IBAction) showAlertsButtonHit:(id)sender; signature to the class interface.

    Open the AlertAppDelegate.m file so we can implement the showAlertsButtonHit: action method:
    - (void) showAlertsButtonHit:(id)sender {
    NSInteger result = NSRunAlertPanel(@"Title",
    @"Message",
    @"Default Button",
    @"Alternate Button",
    @"Other Button");
    result = NSRunCriticalAlertPanel(@"Title",
    @"Message",
    @"Default Button",
    @"Alternate Button",
    @"Other Button");
    result = NSRunInformationalAlertPanel(@"Title",
    @"Message",
    @"Default Button",
    @"Alternate Button",
    @"Other Button");
    NSAlert *alert = [NSAlert alertWithMessageText:@"Alert Title"
    defaultButton:@"Default Button"
    alternateButton:@"Cancel"
    otherButton:@"Other Button"
    informativeTextWithFormat:@"Something bad has happened."];
    [alert runModal];
    alert = [NSAlert alertWithMessageText:@"Alert Title"
    defaultButton:@"Default Button"
    alternateButton:@"Cancel"
    otherButton:@"Other Button"
    informativeTextWithFormat:@"Something bad has happened."];
    [alert beginSheetModalForWindow:[self window]
    modalDelegate:self
    didEndSelector:@selector(sheetModalEnded:returnCode:contextInfo:)
    contextInfo:nil];
    }
    
  4. 4. Double-click on MainMenu.xib to open it in Interface Builder.

  5. 5. Drag a Push Button from the Library palette to the application window.

  6. 6. Connect the Push Button and the button's action method. Right-click on Alert App Delegate and drag a connection from the showAlertsButton outlet to the button in the main window.

  7. 7. Drag a connection from the showAlertsButtonHit: Received Action to the button in the main window.

  8. 8. Back in Xcode, choose Build and Run from the toolbar:

How it works...

Cocoa provides several C functions for quickly creating alerts. When using the C functions to create your alerts, you can determine which button the user has clicked by comparing the return value to one of the following constants: NSAlertDefaultReturn, NSAlertAlternateReturn, and NSAlertOtherReturn.

In the case of using a sheet, you must provide a delegate method that will be called when the alert is closed. This delegate method must have the following signature:

- (void) sheetModalEnded:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo

Although the signature of the method must remain the same, the selector name, sheetModelEnded, can vary. This can be handy when using a single controller to handle many alerts.

The returnCode parameter will determine which button was used to close the alert. You should compare this value to the constants listed above.

There's more...

Using the beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo: method for creating an alert is very flexible. You can display the alert in one part of your application and handle the delegate to close the sheet in another part of the application. You can use the contextInfo: parameter to pass an NSDictionary or maybe an NSArray with values that you might need in determining the proper action to take depending on which button the user clicked in the sheet.

See also

The sample code and project for this recipe is located under the Alert folder in the code bundled with this book.

 

Formatting dates


When building applications, it is often necessary to work with dates. Cocoa provides the NSDateFormatter class for working with dates. NSDateFormatter can convert dates to strings and strings into dates. As we will see in this recipe, NSDateFormatter can also work with relative dates such as "Today" and "Yesterday".

Getting ready

In this recipe, we will create a Cocoa application as we have done in other recipes. However, we will be using NSLog() to display the results on the console rather than the applications window.

In Xcode, let's create a new Cocoa Application project and save it as DateFormat.

How to do it...

In the following code sample, we have used all the possible styles to format the current date. We have also used the setDoesRelativeDateFormatting:YES method so that we can print relative dates.

Add the following code in the applicationDidFinishLaunching: method:

NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setTimeStyle:NSDateFormatterFullStyle];
[formatter setDateStyle:NSDateFormatterFullStyle];
NSLog(@"Full Style: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setTimeStyle:NSDateFormatterLongStyle];
[formatter setDateStyle:NSDateFormatterLongStyle];
NSLog(@"Long Style: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setTimeStyle:NSDateFormatterMediumStyle];
[formatter setDateStyle:NSDateFormatterMediumStyle];
NSLog(@"Medium Style: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setTimeStyle:NSDateFormatterShortStyle];
[formatter setDateStyle:NSDateFormatterShortStyle];
NSLog(@"Short Style: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setTimeStyle:NSDateFormatterNoStyle];
[formatter setDateStyle:NSDateFormatterFullStyle];
NSLog(@"No Time: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setTimeStyle:NSDateFormatterFullStyle];
[formatter setDateStyle:NSDateFormatterNoStyle];
NSLog(@"No Date: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setTimeStyle:NSDateFormatterNoStyle];
[formatter setDateStyle:NSDateFormatterFullStyle];
[formatter setDoesRelativeDateFormatting:YES];
NSLog(@"Relative: %@", [formatter stringFromDate:[NSDate date]]);
[formatter setDateStyle:NSDateFormatterShortStyle];
NSDate *date = [formatter dateFromString:@"9/10/2010"];
NSLog(@"Date String: %@", [date description]);

How it works...

The NSDateFormatter formats the date and time depending on the style constants you set. It's also possible to set no style for either date or time.

There's more...

When converting a string to a date, it is important to remember that the date string should be in the same format as the style you have set in the formatter. You will notice in the code example above that I have the time set to NSDateFormatterNoStyle and the date set to NSDateFormatterShortStyle and my date string is"9/10/2010".

See also

The sample code and project for this recipe is located under the DateFormat folder in the code bundled with this book.

 

Formatting numbers


Applications often have to work with numbers that need to be displayed to the user as percentages or currency and so on. Using Cocoa, formatting numbers is quite easy, as this recipe will show.

Getting ready

Create a new Cocoa Application project and save it as NumberFormat. We will be adding our code to the applicationDidFinishLaunching: method of this applications delegate. As with the Formatting dates recipe, we will see our number formatting on the console rather than in the applications window.

How to do it...

In the sample code, we use the five different number format styles to format an NSNumber object. You can also provide NSNumberFormatter with a NSLocale which will help with localization of numbers. Note how we set a Spanish local when using the NSNumberFormatterSpellOutStyle style. This converts the spelled out number to Spanish.

Add the following code in the applicationDidFinishLaunching: method:

NSNumberFormatter *formatter = [[[NSNumberFormatter alloc] init] autorelease];
NSNumber *number = [NSNumber numberWithDouble:1234.99];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSLog(@"Decimal Style: %@", [formatter stringFromNumber:number]);
number = [NSNumber numberWithDouble:.2];
[formatter setNumberStyle:NSNumberFormatterPercentStyle];
NSLog(@"Percent Style: %@", [formatter stringFromNumber:number]);
number = [NSNumber numberWithDouble:200.95];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSLog(@"Currency Style: %@", [formatter stringFromNumber:number]);
number = [NSNumber numberWithDouble:200.95];
[formatter setNumberStyle:NSNumberFormatterScientificStyle];
NSLog(@"Scientific Style: %@", [formatter stringFromNumber:number]);
number = [NSNumber numberWithDouble:200.95];
[formatter setNumberStyle:NSNumberFormatterSpellOutStyle];
NSLog(@"Spelled Out Style: %@", [formatter stringFromNumber:number]);
NSLocale *locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"es"] autorelease];
[formatter setLocale:locale];
NSLog(@"Spelled Out Style: %@", [formatter stringFromNumber:number]);

How it works...

NSNumberFormater formats strings and numbers in a variety of formats. The number formatter will handle adding number separators, percent signs and currency symbols automatically, based on the locale.

There's more...

NSNumberFormatter also has methods to support rounding numbers if needed. This is done through the setRoundingMode: method.

See also

The sample code and project for this recipe is located under the NumberFormat folder in the code bundled with this book.

 

Importing images


Every Mac OS X application uses images within the user interface. Adding an image using NSImage is pretty straightforward as long as you use one of the support formats. The formats supported by NSImage are: JPEG, TIFF, GIF, PNG, PICT, BMP, ICNS, and ICO.

Getting ready

Lets create a new Cocoa Application project in Xcode and save it as Image.

How to do it...

  1. 1. Open ImageAppDelegate.h so we can add a NSImageView *imageView variable to the class interface. Be sure to add a property for the image view as well:

    @property (retain) IBOutlet NSImageView *imageView;
    
  2. 2. Next, we need to open ImageAppDelegate.m so that we can add the @synthesize imageView; below the synthesized window.

  3. 3. In the applicationDidFinishLaunching: method, lets add the following code:

    NSImage *image = [NSImage imageNamed:@"LearningJQuery.jpg"];
    [imageView setImage:image];
    [imageView setImageScaling:NSImageScaleAxesIndependently];
    [imageView setImageAlignment:NSImageAlignCenter];
    
  4. 4. We need to add the image that we want to import into our Xcode project. We can do this by right-clicking on one of the folders in the project and choosing Add and then choosing Existing Files...

  5. 5. Double-click on MainMenu.xib in Xcode's project view to open the interface file in Interface Builder.

  6. 6. In the Library palette, find the Custom View and drag it into the applications window.

  7. 7. Select the Identity tab in the Inspector palette and set the Class popup to NSImageView.

  8. 8. Right-click on the Image View App Delegate and drag a connection from imageView to the CustomView (note that the custom view is now labeled NSImageView) that you dragged into the application window:

  9. 9. Head back to Xcode and choose Build and Run from the toolbar.

How it works...

The NSImage class' imageNamed: method will search the image cache, your applications main bundle, or the AppKit's bundle for the passed image name and if found, loads it from disk into the NSImage instance. We then call the setImage: method on the NSImageView to set the image instance. The NSImageView will draw the image within its view and possibly scale the image depending on the value set with the setImageScaling: method.

There's more...

You can set different options on how NSImageView will scale the image you provide based on whether your image is larger or smaller than the image view. You can also tell NSImageView not to scale at all.

See also

The sample code and project for this recipe is located under the Image folder in the code bundled with this book.

 

Saving preferences with NSUserDefaults


Many applications have preferences or other options that they need to save between application launches. Cocoa provides the NSUserDefaults class for this very task. As long as your preference is one of the standard C types or one of Cocoa's types, it can be used with NSUserDefaults. The following Cocoa types can be saved with NSUserDefaults: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. Note that the NSArray and NSDictionary can only contain instances of the previous four classes.

Getting ready

In Xcode, create a new Cocoa Application project and save it as UserDefaults.

How to do it...

Open the UserDefaultsAppDelegate.m file and add the following in the applicationDidFinishLaunching: method:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"Publisher"]) {
NSLog(@"Found: %@", [defaults objectForKey:@"Publisher"]);
} else {
NSLog(@"Did not find Publisher.");
}
NSInteger count = [defaults integerForKey:@"Count"];
NSLog(@"Current Count: %d", count);
count++;
[defaults setInteger:count forKey:@"Count"];
[defaults setObject:@"Packt Publishing" forKey:@"Publisher"];

How it works...

The first time that you run this code, it will save two values to the preferences. The first is just a integer count; and the second is the publishers name. If you quit the application and run it again, the application will print "Found: Packt Publishing".

To save the values in the preference system, we use setInteger:forKey: and setObject:forKey: methods. The setObject:forKey: method can save any of the Cocoa types such as NSString, NSArray, NSData and NSDictionary.

There's more...

The standard C types may also be stored in the preference system using methods for the appropriate types such as setInteger:forKey: for integers, setFloat:forKey: for floats, and setDecimal:forKey: for decimals.

See also

The sample code and project for this recipe is located under the UserDefaults folder in the code bundled with this book.

 

Retrieving preferences with NSUserDefaults


In the previous recipe, Saving preferences with NSUserDefaults, we saved two values to the preferences system. In this recipe, we will retrieve those values.

Getting ready

Let's open the UserDefaults project that we used in the previous recipe.

How to do it...

In the UserDefaultsAppDelegate.m file, we have the following code. Add the following code to the applicationDidFinishLaunching: method:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"Publisher"]) {
NSLog(@"Found: %@", [defaults objectForKey:@"Publisher"]);
} else {
NSLog(@"Did not find Publisher.");
}
NSInteger count = [defaults integerForKey:@"Count"];
NSLog(@"Current Count: %d", count);
count++;
[defaults setInteger:count forKey:@"Count"];
[defaults setObject:@"Packt Publishing" forKey:@"Publisher"];

How it works...

Running this code will return the count and the name of the publisher, "Packt Publishing". Each time you run the application, we add one to the count and save it back to the preferences.

We use the integerForKey: method to return the count and objectForKey: to return the publisher as a NSString.

Note that the values returned from the objectForKey: method are immutable, and therefore cannot be modified even if you are using one of the NSMutable variants when saving user defaults.

There's more...

You can examine all application preferences using the defaults command line utility. As an example, we can show hidden applications in the Dock by using the following command:

defaults write com.apple.Dock showhidden -bool YES

For more information on the defaults command, start the Terminal.app and type man defaults.

See also

The sample code and project for this recipe is located under the UserDefaults folder in the code bundled with this book.

 

Adding a password to KeyChain


The KeyChain application provides users with secure methods of storing passwords and certificates in Mac OS X. Cocoa applications can store passwords in KeyChain by using a single method provided by the KeyChain Services interface.

Getting ready

In Xcode, create a new Cocoa Application project and save it as KeyChain.

In order to use the KeyChain Services, we need to add the Security.framework to our Xcode project and add #import <Security/Security.h> to our classes header file.

How to do it...

To store the password in KeyChain, we call the SecKeychainAddGenericPassword() method. Since we are using the default key chain, we can pass NULL for the first parameter. We pass the name of the application for the service name. The account name and password are set. Since we don't need the key chain's item reference, we simply pass NULL for the last parameter.

Lastly, we check the return value to ensure that the password was set without error.

Add the following code to the applicationDidFinishLaunching: method:

status = SecKeychainAddGenericPassword(NULL,
[serviceName length],
[serviceName UTF8String],
[accountName length],
[accountName UTF8String],
[password length],
[password UTF8String],
NULL);

How it works...

The KeyChain Services provided by Cocoa provides a secure method of storing passwords and other protected data using the KeyChain programming interface.

There's more...

After running the sample application, you can start KeyChain Access.app and search for the account name to verify that your password was saved.

See also

The sample code and project for this recipe is located under the KeyChain folder in the code bundled with this book.

 

Retrieving a password from KeyChain


We have seen how to store a password using KeyChain Services in the previous recipe. Now lets retrieve the password that we stored in the previous recipe.

Getting ready

In this recipe, we will continue with the sample application that we created in the Adding a password to KeyChain recipe.

How to do it...

  1. 1. To retrieve the password from KeyChain, we call the SecKeychainFindGenericPassword() method. Since we are using the default key chain we pass NULL for the first parameter. Next, we need to use the same service name and account name that we used when storing the password. The next two parameters are both pointers. The first is the password length, a UInt32, which will be set to the length of the password on return. The second pointer is a void pointer for the password itself:

    status = SecKeychainFindGenericPassword (NULL,
    [serviceName length],
    [serviceName UTF8String],
    [accountName length],
    [accountName UTF8String],
    &bufferLength,
    &buffer,
    NULL);
    
  2. 2. Note that you must free the void password pointer since the KeyChain Services interface has allocated the memory. You free the memory with a call to SecKeychainItemFreeContent() passing NULL for the first parameter and the buffer to free in the second parameter:

    SecKeychainItemFreeContent(NULL, buffer);
    

How it works...

The SecKeychainFindGenericPassword() method searches KeyChain for the password stored under the service name and account name. If the password is found, the return value will equal errSecSuccess and the password will be returned in the allocated buffer.

There's more...

When the SecKeychainFindGenericPassword() is called, KeyChain Services will alert the user asking for permission to access the KeyChain:

See also

The sample code and project for this recipe is located under the KeyChain folder in the code bundled with this book.

 

Accessing the Address Book


The Address Book is a system-wide database of contacts usually managed with the Address Book application. Cocoa applications have access to the contacts in the Address Book application through the ABAddressBook and the ABPerson interface.

Let's add a new organization to the address book and then list all the names and companies in our address book.

Getting ready

In Xcode, create a new Cocoa Application project and save it as AddressBook.

How to do it...

  1. 1. The first thing we need to add to our Xcode project is the AddressBook framework. Right-click on the Frameworks folder in the project view and choose Add, then choose Existing Frameworks. Find AddressBook.framework in the list of frameworks and click on Add.

  2. 2. Next, let's open the AddressBookAppDelegate.h. We need to add #import <AddressBook/AddressBook.h> below the existing import for Cocoa.

  3. 3. Now let's open the AddressBookAppDelegate.m and add the following code:

    ABAddressBook *book = [ABAddressBook sharedAddressBook];
    ABPerson *newPerson = [[[ABPerson alloc] initWithAddressBook:book] autorelease];
    [newPerson setValue:@"Packt Publishing" forProperty:kABOrganizationProperty];
    [book save];
    NSArray *people = [book people];
    for (ABPerson *person in people) {
    NSString *first = [person valueForProperty:kABFirstNameProperty];
    NSString *last = [person valueForProperty:kABLastNameProperty];
    NSString *company = [person valueForProperty:kABOrganizationProperty];
    if (first != nil && last != nil) {
    NSLog(@"Name: %@ %@", first, last);
    } else if (company != nil) {
    NSLog(@"Name: %@", company);
    }
    }
    

How it works...

First, we get a reference to the shared AddressBook, which in this case we will call "book". Next we allocate an ABPerson object for the new company that we are going to add to the address book. We set the value of the new persons kABOrganizationProperty to "Packt Publishing". Finally, we call the save method on the shared address book to save the new person in the contacts.

To get an array of all the people and organizations in the address book, we call the people method on the shared address book's reference. The array will be returned with ABPerson objects, which can then be queried for various properties such as the kABFirstNameProperty and kABLastNameProperty.

There's more...

The ABPerson class can be thought of as a type of dictionary. There are several keys that can be used with the valueForProperty: method to obtain information about a person. Some other properties supported by the ABPerson object are: kABAddressProperty, kABEmailProperty, and kABBirthdayProperty.

See also

The sample code and project for this recipe is located under the AddressBook folder in the code bundled with this book.

 

Adding an event to iCal


iCal is a single database Calendar Store from which you have access to all calendars, events, and tasks. You access these items in the Calendar Store via CalCalendarStore interface.

Getting ready

In Xcode, let's create a new Cocoa Application project and save it as Calendars.

How to do it...

  1. 1. This recipe assumes that you have a calendar called "Book" in iCal. To add the calendar, start the iCal application. Choose File and then choose New Calendar.... When the new calendar is added, name it Book.

  2. 2. Right-click on the Frameworks folder in the project view of Xcode. Choose Add and then choose Existing Frameworks. Select the CalendarStore.framework and choose Add.

  3. 3. Next, lets open the CalendarsAppDelegate.h file and add #import <CalendarStore/CalendarStore.h> below the import for Cooca.h.

  4. 4. Open the CalendarsAppDelegate.m file and add the following code in the applicationDidFinishLaunching: method:

    CalCalendarStore *calendarStore = [CalCalendarStore defaultCalendarStore];
    NSArray *calendars = [calendarStore calendars];
    for (CalCalendar *calendar in calendars) {
    if ([[calendar title] isEqualToString:@"Book"] == YES) {
    NSDate *startDate = [NSDate dateWithString:@"2010-11-05 10:00:00 -0400"];
    NSDate *endDate = [NSDate dateWithString:@"2010-11-05 11:00:00 -0400"];
    CalEvent *event = [CalEvent event];
    [event setTitle:@"Chapter Review"];
    [event setLocation:@"Bill's Office"];
    [event setStartDate:startDate];
    [event setEndDate:endDate];
    [event setCalendar:calendar];
    NSError *error;
    [calendarStore saveEvent:event span:CalSpanThisEvent error:&error];
    break;
    }
    }
    

How it works...

We first get a reference to the default calendar store. We then get an NSArray of all the calendars in the calendar store.

Next, we look at all the calendars in the calendar store for a calendar with a title of "Book". If we find the calendar, we create a new CalEvent instance with a title of "Chapter Review" and set a few other properties. We need to add a starting and ending date for the event, so we use NSDate's dateWithString: method to create both dates. To associate the event with the book calendar, we call the setCalendar: method. The last thing that we need to do is to save the event with the calendar store's saveEvent:span:error: method.

If you run this application and then open the iCal application, the new event will be shown in the Book calendar on the starting date that you set.

There's more...

Through the Cocoa notification mechanism, your application can receive notifications when other applications add, update, or delete calendars, events, or tasks to the calendar store. To receive these notifications you need to register for them with the NSNotificationCenter.

See also

The sample code and project for this recipe is located under the Calendars folder in the code bundled with this book.

About the Author

  • Jeff Hawkins

    Jeff Hawkins has been developing software solutions and applications for 19 years. He has worked for Adobe Systems supporting third-party developers writing plug-ins for FrameMaker on the Macintosh, Windows, and Solaris platforms. He also has worked for a startup delivering prime-time television shows via satellite to television stations across the United States. Jeff currently works in the Tools and Architecture group for ADP Inc. designing and coding solutions for enterprise payroll systems. Jeff has extensive experience working with C, C++, Objective-C, Java, and JavaScript. In his spare time, Jeff enjoys working with Apple's iOS developing mobile applications and games. Jeff is also a private pilot with a seaplane rating and has built and flown his own Van's RV-8 airplane.

    Browse publications by this author
Book Title
Access this book and the full library for FREE
Access now