General Notes


Everything centers around URLs; even files on the hard drive has a URL (NSURL *xmlURL = [NSURL fileURLWithPath:pathToFile];).

Selecting a folder

Just a basic folder selection that is the starting point for a lot of my automation projects.


//NSArray *fileTypes = [NSArray arrayWithObject:nil];
NSOpenPanel *sourceFolderPanel = [NSOpenPanel openPanel];

[sourceFolderPanel setTitle:@"Choose Folder"];
[sourceFolderPanel setMessage:@"Choose the source folder."];
[sourceFolderPanel setDelegate:self];

[sourceFolderPanel setAllowsMultipleSelection:NO];
[sourceFolderPanel setCanChooseDirectories:YES];
[sourceFolderPanel setCanChooseFiles:NO];

int result = [sourceFolderPanel runModalForDirectory:NSHomeDirectory() file:nil];

if (result == NSOKButton) {
NSString *selectedSourceFolder = [sourceFolderPanel filename];
NSLog(@"%@", selectedSourceFolder);
}


Opening an XML file and start parsing it


There are a few things going on here. This selects a file (as opposed to a folder as above), sets the file path to the new starting directory for the application to save for next time, and finally kicks off the parsing process.


- (void)openXMLFile {

NSArray *fileTypes = [NSArray arrayWithObject:@"xml"];
NSOpenPanel *oPanel = [NSOpenPanel openPanel];
NSString *startingDir = [[NSUserDefaults standardUserDefaults] objectForKey:@"StartingDirectory"];
if (!startingDir)
startingDir = NSHomeDirectory();
[oPanel setAllowsMultipleSelection:NO];
[oPanel beginSheetForDirectory:startingDir file:nil types:fileTypes
modalForWindow:[self window] modalDelegate:self
didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
contextInfo:nil];
}

- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
NSString *pathToFile = nil;
if (returnCode == NSOKButton) {
pathToFile = [[[sheet filenames] objectAtIndex:0] copy];
}
if (pathToFile) {
NSString *startingDir = [pathToFile stringByDeletingLastPathComponent];
[[NSUserDefaults standardUserDefaults] setObject:startingDir forKey:@"StartingDirectory"];
[self parseXMLFile:pathToFile];
}
}

- (void)parseXMLFile:(NSString *)pathToFile {
BOOL success;
NSURL *xmlURL = [NSURL fileURLWithPath:pathToFile];
if (addressParser) // addressParser is an NSXMLParser instance variable
[addressParser release];
addressParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[addressParser setDelegate:self];
[addressParser setShouldResolveExternalEntities:YES];
success = [addressParser parse]; // return value not used
// if not successful, delegate is informed of error
}


Traverse Hierarchy of Files


I wrote this as a very, very beginning of porting my old Simple Cataloger application from REALbasic over to Cocoa. There is a way to point to a folder and get everything contained inside, but doing it this way would allow me to catalog items by preset parameters.

OCFileParser.h



//
// OCFileParser.h
// OCDefaultTemplate
//
// Created by Philip Regan on 5/7/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import

#import "Constants.h"

@class OCTextConverter;

@interface OCFileParser : NSObject {

NSString *sourceFolder;
NSString *destinationFolder;

}
- (void) parseFiles;
- (NSString *) chooseFolder:(NSString *)title WithMessage:(NSString *)message;
- (void) parseFolder:(NSString *)folder;
- (void) parseFile:(NSString *)file;

@end


OCFileParser.m



//
// OCFileParser.m
// OCDefaultTemplate
//
// Created by Philip Regan on 5/7/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "OCFileParser.h"

#import "OCTextConverter.h"

@implementation OCFileParser

- (id) init
{
self = [super init];
if (self != nil) {
sourceFolder = NSHomeDirectory();
destinationFolder = NSHomeDirectory();
}
return self;
}

- (void) dealloc
{
[sourceFolder release];
[destinationFolder release];
[super dealloc];
}

- (void) parseFiles {

sourceFolder = [self chooseFolder:@"Choose Folder" WithMessage:@"Choose the source folder."];
destinationFolder = [self chooseFolder:@"Choose Folder" WithMessage:@"Choose the destination folder."];

[self parseFolder:sourceFolder];

}

- (NSString *) chooseFolder:(NSString *)title WithMessage:(NSString *)message {
NSOpenPanel *mOpenPanel = [NSOpenPanel openPanel];

if (title != nil) {
[mOpenPanel setTitle:title];
} else {
[mOpenPanel setTitle:@"Choose Folder"];
}

if (message != nil) {
[mOpenPanel setMessage:message];
} else {
[mOpenPanel setMessage:@"Please choose a folder."];
}

[mOpenPanel setDelegate:self];

[mOpenPanel setAllowsMultipleSelection:NO];
[mOpenPanel setCanChooseFiles:NO];
[mOpenPanel setCanChooseDirectories:YES];

int result = [mOpenPanel runModalForDirectory:NSHomeDirectory() file:nil];

if (result == NSOKButton) {
return [mOpenPanel filename];
} else {
return nil;
}
}


- (void) parseFolder:(NSString *)folder {
NSDirectoryEnumerator *directoryEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:folder];
NSString *finderItem;

while (finderItem = [directoryEnumerator nextObject]) {

[directoryEnumerator skipDescendents];

NSString *fullPath = [folder stringByAppendingPathComponent:finderItem];

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;

NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:fullPath error:&error];
NSString *directoryFlag = [fileAttributes objectForKey:NSFileType];

if ([directoryFlag isEqualToString:NSFileTypeDirectory]) {
[self parseFolder:fullPath];
} else {
[self parseFile:fullPath];
}
}
}

- (void) parseFile:(NSString *)file {

/*
open the file
run OCTextConverter
save the text to the destination folder
*/
}


@end


Opening a text file


Another key starting point for my automation projects. Essentially, there are three ways:
  • NSString *fileContents = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:&error]; All NSString class or instance methods require an encoding as NSStringEncoding.
  • NSFileHandle to get contents of file as just NSData...
  • NSWorkspace, but documentation can only be found on a PDF.

Opening a Text File contained in the project bundle


Basically, another variation of above, but this time with practical code

- (NSString *) stringFromTextFileInBundleWithName:(NSString *)fileName {

NSBundle* myBundle = [NSBundle mainBundle];
NSString *dataPath = [myBundle pathForResource:fileName ofType:@"txt"];
NSURL *dataURL = [NSURL fileURLWithPath:dataPath];
NSError *readError;
NSString *dataString = [NSString stringWithContentsOfURL:dataURL encoding:NSUTF8StringEncoding error:&readError];
if ( !dataString ) {
NSLog(@"fileName:%@, targetReadError: %@", fileName, readError);
}

return dataString;
}


Load and run an Applescript from a Cocoa application


Be sure that the compiled script is added as a bundle resource in Targets.

- (IBAction) runScript:(id)sender {
NSBundle *myBundle = [NSBundle mainBundle];
NSString *scriptSourceFile = [myBundle pathForResource:@"TestScript" ofType:@"scpt"];
NSDictionary *applescriptInitErrors;
NSAppleScript *scriptToRun = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:scriptSourceFile] error:&applescriptInitErrors];
NSDictionary *applescriptRunErrors;
NSAppleEventDescriptor *appleEventDescriptor = [scriptToRun executeAndReturnError:&applescriptRunErrors];
}


Core code for drag and drop functionality in an NSView


Apple’s documentation is very robust, but sometimes too much so. This is taken pretty much verbatim from their docs but only after stripping out everything that wasn’t absolutely needed (and even then, I’m wondering if their isn’t more to be done). Drag and drop operations are easy to implement at their lowest level, and this code shows all that is needed for a simple NSView. This code does not take into consideration any modifier keys. To change the target area, just change the superclass to the class of choice.

.h



#import


@interface OCReceiveFileMgr : NSView {
@private

}

@end


.m



#import "OCReceiveFileMgr.h"


@implementation OCReceiveFileMgr

- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
}

return self;
}

- (void)dealloc
{
[super dealloc];
}

- (NSDragOperation)draggingEntered:(id )sender {

NSPasteboard *pboard;
NSDragOperation sourceDragMask;

sourceDragMask = [sender draggingSourceOperationMask];
pboard = [sender draggingPasteboard];

if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
return NSDragOperationLink;
}

return NSDragOperationNone;

}

- (BOOL)performDragOperation:(id )sender {

NSPasteboard *pboard;
NSDragOperation sourceDragMask;

sourceDragMask = [sender draggingSourceOperationMask];
pboard = [sender draggingPasteboard];

if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {

NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];

NSLog(@"files: %@", files); // Success!
}

return YES;
}

@end


Converting between HFS (colon-delimited) and POSIX (slash-delimited) paths


When working with Scripting Bridge, it is important to remember that Applescript prefers HFS paths over POSIX paths in almost all cases. These two methods should help make the flip easier.

- (NSString *) convertPosixPathtoHfsPath:(NSString *)posixPath isDirectory:(BOOL)isDirectory {

NSString *firstChar = [posixPath substringToIndex:1];
if ( ![firstChar isEqualToString:@"/"] ) {
return posixPath;
}

CFURLRef myURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)posixPath, kCFURLPOSIXPathStyle, isDirectory);
NSString *hfsPath = (NSString *)CFURLCopyFileSystemPath(myURL, kCFURLHFSPathStyle);
CFRelease(myURL);
return hfsPath;

}

- (NSString *) convertHfsPathtoPosixPath:(NSString *)hfsPath isDirectory:(BOOL)isDirectory {

NSString *firstChar = [hfsPath substringToIndex:1];
if ( [firstChar isEqualToString:@"/"] ) {
return hfsPath;
}

CFURLRef myURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)hfsPath, kCFURLHFSPathStyle, isDirectory);
NSString *posixPath = (NSString *)CFURLCopyFileSystemPath(myURL, kCFURLPOSIXPathStyle);
CFRelease(myURL);
return posixPath;

}