Next article: Custom NSCells Done Right
Previous article: Fluid Simulation for Dummies
Tags: cocoa injection plugin simbl
Occasionally, we wish our favorite programs would sport a particular feature that just isn't there. Those of us who aren't developers need to fall back on lobbying for features, but those of us fortunate enough to be in the biz, churning out line after line of Objective-C well into the heart of the night, waking up the next day with bloodshot eyes and feeling not unlike the living dead, have other options. O, we lucky few.
One such option is SIMBL (pronounced "cymbal"), which is a Cocoa plugin loader for OS X. Once a user has SIMBL installed, you can create XCode projects of type "Cocoa Bundle", compile them and drop the output in either "~/Library/Application Support/SIMBL/Plugins" or "/Library/Application Support/SIMBL/Plugins" on the user's machine. "What's the point?", you may ask. By doing this, any code you write inside your plugin project will have access to any objects that belong to the application you want your plugin to load for. In other words, you can use SIMBL plugins to enhance Cocoa applications! I'll pause for a moment as the full ramifications of this idea wrap their tendrils around your consciousness.
Alright. So once I understood everything I described above, the concept I had the hardest time grasping was how to know what objects resided in applications I didn't write. Well, as it turns out, you don't need to know much about proprietary objects that reside in specific applications, because you can already do a whole lot with generic ApplicationKit objects that exist in nearly every Cocoa application, such as the menu bar, the main window, etc. For the sake of clarification, here's an example: say you're in a strange mood and you decide you really want to inject a new menu item into the Safari main menu that tells you the time in India. Assuming you already have a working subclass of NSMenuItem defined called InternationalClock, the following code snippet makes sense:
// Create our clock.
NSMenuItem *indiaClock = [InternationalClock clockForCountry: @"India"];
// Inject it.
NSMenu* safariMenuBar = [[NSApplication sharedApplication] mainMenu];
[safariMenuBar addItem: indiaClock];
If you want to get more specific and modify proprietary Safari objects, things get a little hairier -- you'll need to know what custom classes are defined in Safari, perhaps what instance variables exist in these classes (1), and certainly what methods you may invoke. For this, I would recommend a great CLI utility called class-dump, which is also mentioned on the wonderful wiki at http://www.culater.net/wiki/moin.cgi/CocoaReverseEngineering. class-dump lists custom classes, along with their corresponding method names and instance variables for any Cocoa application you pass it, and I'm willing to bet that it could suffice as the only utility that most developers need to accomplish their plugin-writing goals.
(1) -[NSObject valueForKey:] is a very handy method for retrieving object attributes, such as private instance variables, from inside your custom bundle.
Thanks for this tip, Mike!
Comments RSS feed for this page
Add your thoughts, post a comment:
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.