Cocoa Programming for Mac OS X - Aaron Hillegass [135]
Figure 36.2. Setting the UTI
In MyDocument.h, create outlets for an NSTableView and an NSArray for holding the filenames in the zip file:
@interface MyDocument : NSDocument
{
IBOutlet NSTableView *tableView;
NSArray *filenames;
}
@end
Open MyDocument.xib. Add a table view to the window, and set it to have one uneditable column with the title Filenames. Control-click on the table view to bring up its Connection panel. Make the dataSource outlet point to File’s Owner (Figure 36.3).
Figure 36.3. Set dataSource Outlet
Control-click on File’s Owner to bring up its connection window. Drag to set the tableView outlet (Figure 36.4).
Figure 36.4. Set tableView Outlet
In MyDocument.m, remove the default readFromData:ofType:error: and override readFromURL:ofType:error: to create an NSTask that executes zipinfo. Also, create an NSPipe and connect it to the standardOut of the NSTask (Figure 36.5)
Figure 36.5. Object Diagram
Here is the code:
- (BOOL)readFromURL:(NSURL *)absoluteURL
ofType:(NSString *)typeName
error:(NSError **)outError
{
// Which file are we getting the zipinfo for?
NSString *filename = [absoluteURL path];
// Prepare a task object
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/bin/zipinfo"];
NSArray *args = [NSArray arrayWithObjects:@"-1", filename, nil];
[task setArguments:args];
// Create the pipe to read from
NSPipe *outPipe = [[NSPipe alloc] init];
[task setStandardOutput:outPipe];
// Start the process
[task launch];
// Read the output
NSData *data = [[outPipe fileHandleForReading]
readDataToEndOfFile];
// Make sure the task terminates normally
[task waitUntilExit];
int status = [task terminationStatus];
// Check status
if (status != 0) {
if (outError) {
NSDictionary *eDict =
[NSDictionary dictionaryWithObject:@"zipinfo failed"
forKey:NSLocalizedFailureReasonErrorKey];
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain
code:0
userInfo:eDict];
}
return NO;
}
// Convert to a string
NSString *aString = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
// Break the string into lines
filenames = [aString componentsSeparatedByString:@"\n"];
NSLog(@"filenames = %@", filenames);
// In case of revert
[tableView reloadData];
return YES;
}
Now you need table view data source methods:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)v
{
return [filenames count];
}
- (id)tableView:(NSTableView *)tv
objectValueForTableColumn:(NSTableColumn *)tc
row:(NSInteger)row
{
return [filenames objectAtIndex:row];
}
Your application doesn’t save, so you can delete the method dataOfType:error: if you wish. Also, you can open up the MainMenu.xib file and delete any menu items that are concerned with saving.
Build and run your application. You should be able to see the contents of any zip file. (No Untitled document will appear (this is a viewer) so you must open an existing .zip file.)
Asynchronous Reads
As mentioned in Chapter 24, the run loop is the object that waits for events, which may be keyboard, mouse, or timer events. These are all run loop data sources. You can also make a file handle a run loop data source.
In this section, we are going to fork off a process that burps up data occasionally. We will attach a pipe to standardOut, but instead of trying to read all the data from the file handle immediately, we will ask the file handle to read in the background and send a notification when data is ready.
You can use /sbin/ping to check whether you can make an IP connection to another machine. Try running it in Terminal:
$ /sbin/ping -c10 www.bignerdranch.com
PING www.bignerdranch.com (69.39.89.150): 56 data bytes
64 bytes from 69.39.89.150: icmp_seq=0 ttl=50 time=35.579 ms
64 bytes from 69.39.89.150: icmp_seq=1 ttl=50 time=35.099 ms
64 bytes from 69.39.89.150: icmp_seq=2 ttl=50 time=34.546 ms
64 bytes from 69.39.89.150: icmp_seq=3