How to Modify the Dock or Login Items on OS X
If you need to programmatically add or remove applications from the user’s Dock or Login Items, you’ll find that there isn’t a standard way to do both that works on OS X 10.4 and later systems.
Apple provided some early sample code for handling login items called LoginItemsAE. The code is complicated and does not work consistently for me.
CocoaDev has a page that talks about starting an application on startup. I agree that launchd user agents do not work well on 10.4.
If you no longer need to support 10.4, you can use the LSSharedFileList
API that Apple introduced in 10.5 to manage Login Items. Information on that API does not yet appear in the official documentation, but you can find example code here and here.
As far as I know, Apple still does not provide a way to easily manage Dock items. I believe this is by design. The Dock is supposed to controlled by the user. However, I’ve found that users sometimes want an application to add or remove itself from the Dock for convenience.
Login Items
This code uses NSUserDefaults
to modify the settings files for the user’s Login Items. If the System Preferences application is open, it must be closed and reopened to see the changes.
@implementation NSUserDefaults (Additions)
- (BOOL)addApplicationToLoginItems:(NSString *)path {
NSDictionary *domain = [self persistentDomainForName:@"loginwindow"];
NSArray *apps = [domain objectForKey:@"AutoLaunchedApplicationDictionary"];
NSArray *matchingApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"Path CONTAINS %@", path]];
if ([matchingApps count] == 0) {
NSMutableDictionary *newDomain = [domain mutableCopy];
NSMutableArray *newApps = [[apps mutableCopy] autorelease];
NSDictionary *app = [NSDictionary dictionaryWithObjectsAndKeys:path, @"Path", [NSNumber numberWithBool:NO], @"Hide", nil];
[newApps addObject:app];
[newDomain setObject:newApps forKey:@"AutoLaunchedApplicationDictionary"];
[self setPersistentDomain:newDomain forName:@"loginwindow"];
return [self synchronize];
}
return NO;
}
- (BOOL)removeApplicationFromLoginItems:(NSString *)name {
NSDictionary *domain = [self persistentDomainForName:@"loginwindow"];
NSArray *apps = [domain objectForKey:@"AutoLaunchedApplicationDictionary"];
NSArray *newApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"not Path CONTAINS %@", name]];
if (![apps isEqualToArray:newApps]) {
NSMutableDictionary *newDomain = [domain mutableCopy];
[newDomain setObject:newApps forKey:@"AutoLaunchedApplicationDictionary"];
[self setPersistentDomain:newDomain forName:@"loginwindow"];
return [self synchronize];
}
return NO;
}
@end
Dock
The code for adding applications to and removing them from the user’s Dock is very similar. The differences include the domain, key, predicate and dictionary entry.
This code uses NSUserDefaults
to modify the settings files for the user’s Dock. The Dock must also be restarted after making a change. The command “killall Dock” works, either programmatically or from the Terminal.
@implementation NSUserDefaults (Additions)
- (BOOL)addApplicationToDock:(NSString *)path {
NSDictionary *domain = [self persistentDomainForName:@"com.apple.dock"];
NSArray *apps = [domain objectForKey:@"persistent-apps"];
NSArray *matchingApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K CONTAINS %@", @"tile-data.file-data._CFURLString", path]];
if ([matchingApps count] == 0) {
NSMutableDictionary *newDomain = [domain mutableCopy];
NSMutableArray *newApps = [[apps mutableCopy] autorelease];
NSDictionary *app = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObjectsAndKeys:path, @"_CFURLString", [NSNumber numberWithInt:0], @"_CFURLStringType", nil] forKey:@"file-data"] forKey:@"tile-data"];
[newApps addObject:app];
[newDomain setObject:newApps forKey:@"persistent-apps"];
[self setPersistentDomain:newDomain forName:@"com.apple.dock"];
return [self synchronize];
}
return NO;
}
- (BOOL)removeApplicationFromDock:(NSString *)name {
NSDictionary *domain = [self persistentDomainForName:@"com.apple.dock"];
NSArray *apps = [domain objectForKey:@"persistent-apps"];
NSArray *newApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"not %K CONTAINS %@", @"tile-data.file-data._CFURLString", name]];
if (![apps isEqualToArray:newApps]) {
NSMutableDictionary *newDomain = [domain mutableCopy];
[newDomain setObject:newApps forKey:@"persistent-apps"];
[self setPersistentDomain:newDomain forName:@"com.apple.dock"];
return [self synchronize];
}
return NO;
}
@end
If you need to manage both the Dock and the user’s Login Items. You can put all four methods into one category.
I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.
Comments are closed.