Recipe #3: Adding your app to Login Items using LSSharedFileList
Mar0
I’ve been developing an utility app. I’m really excited about this and I do hope to release it quite soon (still have some issues with choctop and sparkle stuff). I even got an icon for it (thanks, Scarlet Bits!). In this blog post I’d like to share about a solution to a problem that I faced while developing my app – launching it at the beginning of user’s session, i.e. on login.
Since Mac OS X Leopard (10.5) apple has dedicated API for these kind of things. And by writing “kind of” I’m going to quote the Apple developer documentation itself:
The Shared File List API is new to Launch Services in Mac OS X Leopard. This API provides access to several kinds of system-global and per-user persistent lists of file system objects, such as recent documents and applications, favorites, and login items. For details, see the new interface file LSSharedFileList.h.
Read on if you’d like to know how to add your app to login items list.
Getting login items list
First off – we’ll fetch login items to see if our app is already in that list. Well, at first it won’t be there, but it will pay off in the future, trust me. So here’s the code snippet for fetching existing login items and assigning them to an NSArray:
// Some seed data UInt32 seedValue; // Let's create reference to shared file list LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); // Then just pop values from referenced list into array NSArray *loginItemsArray = (NSArray *)LSSharedFileListCopySnapshot(loginItems, &seedValue);
You can now use this array as any table’s data source to see what this list contains. If you’re too lazy, then I can tell you that each item has “assigned display name, icon, and url as well as other optional properties” (quote from source code).
Now that we’ve got the list of login items – it’s really easy to add/remove our app to/from the list of login items. But for starters – we’re going to check for item’s presence in the list.
Checking for item’s existence
Since it is my first experience with login items and LSSharedFileList, from what I found on the net, I believe that main criteria is to check item’s url against given one. This code snippet shows how it should be done:
- (BOOL)loginItemExistsWithLoginItemReference:(LSSharedFileListRef)theLoginItemsRefs ForPath:(CFURLRef)thePath { BOOL exists = NO; UInt32 seedValue; // We're going to grab the contents of the shared file list (LSSharedFileListItemRef objects) // and pop it in an array so we can iterate through it to find our item. NSArray *loginItemsArray = (NSArray *)LSSharedFileListCopySnapshot(theLoginItemsRefs, &seedValue); for (id item in loginItemsArray) { LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item; if (LSSharedFileListItemResolve(itemRef, 0, (CFURLRef*) &thePath, NULL) == noErr) { if ([[(NSURL *)thePath path] hasPrefix:@"/Applications/MyApp.app"]) exists = YES; } return exists; };
Adding an item to login items list
Method for adding an item to login items list is LSSharedFileListInsertItemURL. – method for inserting. This method accepts 7 parameters, but only 3 of them will be of interest to us. These are the accepted parameters:
- Reference to login item list (created in the code above. Do not mix up with array of items)
- Position of where to insert new login item. To insert item to the beginning of the list use
kLSSharedFileListItemBeforeFirst. To insert item to the ending of the list usekLSSharedFileListItemLast. Otherwise passLSSharedFileListItemRef. - Display name of new login item. Can be NULL. Defaults to app name.
- Icon of the login item. Can be NULL. Defaults to app icon.
CFURLRefof item to insert. This usually is a path to your app (like /Applications/MyApp.app)CFDictionary– dictionary of options for new login item.CFDictionary– dictionary of options to clear if item already exists in the list.
The ones interesting for us are 1, 2 and 5. So the code for adding an item to login items list could be as follows:
// Reference to shared file list LSSharedFileListRef theLoginItemsRefs = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); // CFURLRef to the insertable item. CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:@"/Applications/MyApp.app"]; // Actual insertion of an item. LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(theLoginItemsRefs, kLSSharedFileListItemLast, NULL, NULL, thePath, NULL, NULL); // Clean up in case of success if (item) CFRelease(item);
Removing an item from login items list
Method for removal of item in question is LSSharedFileListItemRemove. This method contrary to the one for adding the item to login items list accepts only two parameters:
- The reference to login items list
- The item reference in question
It could not get simpler than this:
LSSharedFileListItemRemove(theLoginItemsRefs, itemRef);
Conclusion
This is the first part of managing login items in Mac OS X. In next part I’ll show you how to keep track of changes while your app is not running and adding/removing your app to/from login items isn’t handled by your app.
Please note that I found code samples in one place on the internet but was too lazy to bookmark it, hence this code was extracted from the app. If you recognize your stuff – please post a note/link in the comments. Thanks a heap for reading this!
Recipe #2: Initializing user preferences using NSUserDefaults
Jan2
Recently I was in need of some user preferences. I read a guide on user defaults programming. Everything seemed easy and clear, but I still couldn’t find the thing that interested me most – initializing user preferences. By “initialization” I mean set defaults once so they can be overridden by user and not set again. For some reason I thought that I’d have to check each preferences key for its existence and set it accordingly.
Then the light bulb lit above my spiky head. I present you with the solution:
// Place this code into your apps delegate class + (void)initialize { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // initialize the dictionary with default values NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:@"fooValue", @"fooKey", @"barValue", @"barKey", nil]; // and set them appropriately [defaults registerDefaults:appDefaults]; }
Now for refactored and more comfortable version you should use a separate plist for initializing user defaults. I believe it’s easier to add a row to plist file than add key/value pair into dictionary:
+ (void)initialize { // get the filename for "default defaults" NSString *defaultsFilename = [[NSBundle mainBundle] pathForResource:@"Defaults" ofType:@"plist"]; // initialize a dictionary with contents of it NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:defaultsFilename]; // register the stuff [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; }
The advantage of this approach is that even after updates to your app even unset defaults will be initialized (in case of newly added preferences).
That’s it! Kudos goes to Laurent Etiemble and Johan Kool for their answers to my question.
XCode file template for custom NSWindow subclass
Dec0
This has been for a while now in my drafts and I wanted to polish it so it wouldn’t require as much editing as some previous tutorials. I hope you’ll enjoy reading this one as much as I enjoyed writing this. I present you – custom NSWindow template tutorial.
From time to time while programming you’ll have to draw custom window. If it’s more often than “from time to time” you’ll find yourself writing the same code again and again. Of course it’s easy to extract repetitive code into separate class and include that class in every project (or few times into the same project), but this still not the prettiest solution. Im suggesting you an approach that exploits XCode’s customization – template for custom class that you’ll be able to include into your projects anytime you need it. In this case – it’s custom NSWindow subclass.
So here’s how we’re going to achieve this:
- Create project in XCode where we will code a template for custom NSWindow. If you are really good with Objective C – you can code our custom class in any text editor (TextEdit, TextMate) or in an IDE of your choice.
- Create custom NSWindow class
- Replace hardcoded constants (class names) with identifiers
- Move created class files to a location where XCode can consider them as being custom template files
git for everyone!!!111!!ONE
Dec0
I’ve found really nifty site today for dealing with git source control management system. It’s git ready. I think I may be one of the last ones to find it, cause it has loads of articles on different topics. You should definitely check it out if you haven’t yet.
Quickly change desktop background pt. 2
Nov1
Few weeks ago I wrote about writing an App for quickly changing background for Mac OS X desktop.
Things that’ve changed:
- Up to 10 items in history list (suggestion from Perspx).
- App is UI agent now (pseudo-background app). Activates via ⌘+⌥+⌃+B.
To download click this link: Change Background 0.0.2
Switch between header and implementation files
Nov0
In addition to most common key combination for switching between header and implementation files in xcode you can do that by swiping three fingers up or down.
Neato!!!
Get difference between two NSDate objects
Nov0
One of the most often asked questions is how to get difference between two NSDate objects. Since one of the most useful advices I’ve got while learning cocoa is using what’s there, I’d suggest all the folks look carefully at NSDate class reference documentation and see what it provides. Then just fireup the XCode and write this code into some method:
NSTimeInterval interval = [endDate timeIntervalSinceDate:startDate];
I presume you DO know how to get endDate and startDate object. Otherwise you shouldn’t be reading this. You’ll get the difference in seconds with fractions which is pure double. So you can do with it whatever you want. Feed it to some fancy number formatter, start countdown or what not. None of my business ,)
Oh, and btw, here’s a demo project for you to see this code in action: DateDifference XCode project.
How to listen to all possible notifications in 7 lines of code
Nov2
In the last post I mentioned about setting the background to your dragged image and sending the appropriate [private] notification so that Mac OS X would actually change it. At first it didn’t work so I thought that this notification is no longer valid (considering the fact that it’s private). So I’ve setup a little app for myself which listens to all possible notifications while run.
Main snippet that does all the job is put in 7 lines of code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSDistributedNotificationCenter *dnc = [NSDistributedNotificationCenter defaultCenter]; [dnc addObserver:self selector:@selector(processNotification:) name:nil object:nil]; } - (void)processNotification:(id)notification { NSLog(@"Notification: %@", notification); }
So here’s another idea for another developer tool. Maybe there’s one that already exists, but it’d be really cool to have an app that registers all your your wanted notifications (either by name, app or any other parameter).
Quickly change desktop background
Nov2
Saturday morning. Can of RedBull. Cocoa. And me. Came up with a simple idea to develop an app for quickly changing desktop background. Just drag an image on an app window and you’re done!
Had to google around for some tips and tricks. First results disappointed me much since they referenced usage of Carbon and AppleEvents. Oh yeah, and they were dated back to 2004 or so. Then one post here had what I needed: a ChangeBackground and a [private] notification.
After couple of hours of trial and error I came up with this application that you can download and try out. I think I’m going to work on this app a bit more so it’d become more decent product.
It’s so rough that I can’t guarantee it will work on your Mac. But here’s the requirements so far:
- Snow Leopard
Download and [try to] enjoy. Needless to say – I accept any feedback.
UPDATE: Perspx found a bug which I successfully reproduced and fixed. You can download updated version here.
Centering NSWindow in Cocoa
Oct1
I was messing around with Cocoa. Building some dummy app to try out GData framework. I was creating preferences window and each time I ran the application – preferences window was showing somewhere not in the center of the screen. Yes, I know I can adjust the position via Size inspector for the window. Still, the adjustment area is so small I couldn’t adjust it correctly. And my OCD wanted pixel-perfect positioning. iGoogled and all I found was this NSWindow instance method:
[window center];
Note: it does not show your window on screen. This method only sets it’s frame to the center of the main (read – the one that has menu bar on top) screen.