GreenStripes: Ruby bindings for libspotify

April 15, 2009

About a week ago, Spotify released libspotify, a C API for writing applications that utilize their service.

Now, while I’m certainly not averse to C, I think that a more human-friendly language is a better fit for many tasks. So I whipped up a set of Ruby bindings that lets you write programs like this one:

session = GreenStripes::Session.new(APPLICATION_KEY, 'GreenStripes', 'tmp', 'tmp')
session.login(USERNAME, PASSWORD)
session.process_events until session.connection_state == GreenStripes::ConnectionState::LOGGED_IN

search = GreenStripes::Search.new(session, 'yakety sax', 0, 100)
session.process_events until search.loaded?
puts "found #{search.num_tracks} tracks"
if search.num_tracks > 0
  track = search.track(0)
  session.process_events until track.loaded? and track.artist(0).loaded?
  puts "the first one is #{track.name} by #{track.artist(0).name}"
end

session.logout
session.process_events until session.connection_state == GreenStripes::ConnectionState::LOGGED_OUT

GreenStripes is very much in a beta stage, lacks features and has bugs, but it’s gotten to a point where you can actually play around with it and build stuff.

Interested? GreenStripes is available in source or gem form from GitHub, where you can also read installation instructions and sign up to be notified of updates:

Cocoa sans Xcode and Interface Builder

March 3, 2009

Something that has always bugged me about Cocoa development is that it’s so tightly coupled with Xcode and Interface Builder. Virtually every single guide on the topic will use those two apps, and if you dare ask how one would go about without them, the answer will almost certainly be an exasperated “why would anyone want to do that?!”

And that’s something that bugs me even more. Good programmers love taking things apart to figure out what makes them tick, and I strive to be one. It’s not that I hate Xcode and Interface Builder, but rather that I like to understand what’s going on.

Turns out it’s not tricky at all.

Before we get started, let’s define what developing a Cocoa app without Xcode or Interface Builder actually means. No Xcode means we can use any editor and any build system we want. No Interface Builder means that we’ll be creating our views programmatically. Of course, these are three completely separate topics, and it’s quite possible to do just one or two of them. But let’s go the whole hog.

Objective-C, being a strict superset of C, has to obey every rule that C obeys. Hence, a truly minimal Objective-C program would be something along the lines of:

int main(int argc, char *argv[])
{
  return 0;
}

Of course, this doesn’t do anything interesting, and crucially, it’s not a Cocoa app.

Now, what makes a Cocoa app? Well, if you create an empty Cocoa app project in Xcode, main will contain a call to NSApplicationMain and nothing else. So, I suppose we could just stick that in our main, call it done and go out to lunch. But that would be cheating. Besides, what does NSApplicationMain do anyway?

It actually has an entry in the API reference, but that will not tell you much about what it does. That information is tucked away, for some reason, in the docs for the NSApplication class.

With this knowledge, and considering we’re not going to load any nibs, we can change the code for our minimal Cocoa app into something like this:

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
  [NSApplication sharedApplication];
  [NSApp run];
  return 0;
}

Save the above in a file called kakao.m and compile it with gcc like so:

$ gcc -framework Cocoa -o Kakao kakao.m

You can even run the app if you feel like it:

$ ./Kakao

Although, as you might have noticed, that won’t do much except hang until you kill it. So let’s add something more interesting, like a window.

After a quick look in the docs for the NSWindow class, we change the code for our app into this:

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
  [NSApplication sharedApplication];
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSUInteger style = NSTitledWindowMask
                   | NSClosableWindowMask
                   | NSMiniaturizableWindowMask
                   | NSResizableWindowMask;
  NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,400,400)
                                                 styleMask:style
                                                   backing:NSBackingStoreBuffered
                                                     defer:NO];
  [window makeKeyAndOrderFront:nil];
  [pool drain];
  [NSApp run];
  return 0;
}

Note that we create an NSAutoreleasePool before we create the window, and drain it afterwards. This is something Cocoa will usually take care of, but since we are not yet in an event loop, we have to do it manually.

Compile and run the program like before, and lo, a window appears!

However, this is clearly not a trüe Cocoa app yet. It doesn’t get its own icon in the dock or in the cmd-tab view, and if you try launching it from the Finder, things just get plain silly.

Of course, this is something we can fix. Applications, as well as many other kinds of bundles, are nothing but directories with special names in Mac OS X.

If you’ve ever peeked inside a .app bundle (right-click, Show Package Contents), you know it contains a directory called Contents, which contains among other things a directory called MacOS, which contains the actual executable for the app.

So, without further ado:

$ mkdir -p Kakao.app/Contents/MacOS
$ mv Kakao Kakao.app/Contents/MacOS/

And presto! You have a bonafide Cocoa app, complete with icon and menu bar (which is empty, but still) that can be launched from the Finder.

Now, if you have been digging around .app bundles before, you know that there are two more files that should go in there: Info.plist and PkgInfo. And yes, if you’re developing an actual app you plan on having actual users for, you should create those files as well. But for our purposes, we’ll do fine without them as long as the executable has the same name as the bundle, minus the “.app” suffix.

Oh, and in case you’re wondering, “kakao” is Swedish for “cocoa”.

Recommended reading

This is based on a presentation I gave at CocoaHeads Stockholm. If you’re into Cocoa, and live in or near Stockholm, you should drop by and say hi.