An Objective C Interface to Directory Services

Introduction

I needed to use Directory Services to look some information up. So of course I turned to Core Mac OS X and Unix Programming by Mark Dalrymple and Aaron Hillegass. Before describing the API, they write:

     It seems that Apple let the summer intern write the API. DirectoryService.framework is the most awkward-to-use framework in the entire system.

And they're right. So I thought I would make an Objective C class that wraps around the awkward API. I implemented only enough of the API that I needed. If you need to do more, then add the rest yourself. I'm not here to do your work for you.

Rant about the book

Core Mac OS X and Unix Programming by Mark Dalrymple and Aaron Hillegass is the best Mac OS X programming book out there. It goes beyond the "learn how to program Mac OS X" and into "now that you're not a pansy-assed baby programmer anymore, here's some real stuff that will turn you into a man without all the gratuitous sex". I read the book from cover to cover. I encountered stuff that made me either think, "That thing I did before was all wrong, I should do it this way", or, "Oh, here's how I do that think I was trying to figure out." I bought the book when it was selling for an astonishing $98. Now you can buy it for $65 from Amazon or someone else. I will mock those who do not buy this book.

Directory Services

Directory Services is like a hierarchial database that only goes 2 levels deep. At the top level, you have a number of types. Typical types on your Mac OS X system are aliases, config, groups, users, as seen in NetInfo Manager. For each type, there are a number of records, uniquely identified by name. Each record then has a number of value/attribute pairs. Each value can be a single value or an array of values.

If you need to access Directory Services, you'll probably be reading from it instead of writing to it. That's all my API does.

The API

This is what my API looks like:

@interface NSDirectoryServices : NSObject
{
	tDirReference			_dirRef;
	NSArray*				_nodes;
	tDirNodeReference		_nodeRef;
}
- (NSArray*) getNodes;
- (NSArray*) getRecordsOfType:(NSString*)recordType fromNode:(NSString*)node;
- (NSDictionary*) getRecord:(NSString*)recordName andType:(NSString*)recordType fromNode:(NSString*)node;
@end
The Directory Services API uses nodes. I don't know why anyone would care about this, so I did my best to hide it. You can get a list of nodes, and pass a node to some of the methods. But if you don't, the API will do the "thing you probably intended".

For my purposes, I wanted to get a particular attribute from a particular record. I'm not going to tell you which one. It's secret. So I'm going to make something up for an example. For the example, I want to read the attribute "numberOfGoldfish" from the record "Googenheim", which is a record of type "bulb".

So this is what I would do:

{
	NSDirectoryServices* dirServ = [[NSDirectoryServices alloc] init];
	NSDictionary* fields = [dirServ getRecord:@"Googenheim" andType:@"dsRecTypeNative:bulbs" fromNode:nil];
	NSString* numGoldFish = [fields objectForKey:@"numberOfGoldfish"];
	[dirServ release];
}

This is how simple the directory services API should have been, but instead there are hundreds of lines of code to make this work. -[NSDirectoryServices getRecord:andType:fromNode:] returns a dictionary containing all the attributes and values of the given record and type, looking in the given node. I always pass nil for the node.

Values can either be a single value or an array of values. For single values, the dictionary will contain an NSString. For multiple values, the dictionary will contain an NSArray of NSStrings. The example code above assumes the value is a single value.

Notice how the type is "dsRecTypeNative:bulbs". For standard types, the prefix of the type will be "dsRecTypeStandard:" (kDSStdRecordTypePrefix). All the standard types are defined in DirServicesConst.h. For any types that are not standard, you need to prefix the type with "dsRecTypeNative:" (kDSNativeRecordTypePrefix).

The other method I made was [NSDirectoryServices getRecordsOfType:(NSString*)recordType fromNode:(NSString*)node]. Obviously, given a type, it will return an array of the names of all the records. It accomplishes this by iterating over all the records of a given type, and fetching it's name attribute. I'm not going to show you an example since I think it's obvious and I'm a very busy man.

Download the code

I did all the work, and now you can download free code. Lucky you. Consider sending me money.

Download (4K)

Extreme Mac OS X Programming. Copyright 2004 John A. Vink