Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-143
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 143
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 143

Using Python and Ruby to Build Cocoa Applications

Application Technologies • 33:53

Cocoa's dynamic nature makes it a natural fit for modern, object-oriented scripting languages such as Python and Ruby. Open Source scripting bridges provide a powerful tool for coders to experiment with dynamic interpreters, while scripters can tap into the richness and power of native application frameworks. This session will cover everything from basic prototyping to constructing complete standalone applications using AppKit, Core Data and other high-level frameworks, using Python and Ruby.

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

So welcome to Cocoa from Ruby and Python. We've got some pretty exciting stuff to talk about here. I'm Bill Baumgartner. I'm the manager of the sort of chewy middle layers in Xcode, build system, things like that. And, you know, scripting languages have become really hot. I mean, they've been hot for a while, but they're really getting out there. They're becoming very popular.

And people are using them to build larger and larger things as well. Now, some of the reasons for this popularity is that they really offer a rapid application development methodology or patterns that you just can't do with compiled languages. Likewise, scripting languages are often considered to be batteries included, meaning that the scripting languages come with these huge libraries.

They're really easy to use, and they do some very powerful things. Or you can pick up libraries from third parties. And I am. I asked the O'Reilly. O'Reilly folks for some numbers, and they were very kind to give it to me. These are book sales in the last year and changes over time.

And as you can see, Pearl's off a little bit, but still a pretty good-sized chunk. Ruby's up 700%, largely because of Rails. Python's climbing, even though nothing's really happened in the last year. So, yeah, scripting languages, they're here, and everyone's using them. And Cocoa developers really shouldn't be left out of this fun.

So why? Why bother? Well, it gets down to the rapid application development thing. Thanks for the demo machine. When we were doing Core Data, one of the biggest challenges when you're coming up with a new API is, can you use it? Does it feel comfortable? So literally within the first, oh, I don't know, week or so of having an Xcode project that was the Core Data framework, I was able to bring up my Python shell. import the foundation because well you always have to have a foundation and then i could um let me grab objective c too i could then just load bundle I don't remember how to use Load Bundle.

It's another great Rapid App development feature. I can actually get help. Oh, I need to see the help though. So if I do, obviously, load bundle, core data, I'm going to just throw it right into my interpreter because, well, that's a useful place for it. I can now go off and I could, you know, create a managed object model.

I could work with the APIs. I could do whatever. And then because of what I'm going to show you next, I could then turn around and turn these into unit tests. I could turn them into a full-blown app. I could do all my prototyping. So you're going straight from interacting with your live objects through to building production apps. Can I have the slides, please? So this is our commitment.

We, Apple, are embracing scripting languages as first-class application development tools. This includes binary compatibility across releases. So... If you build an app with Ruby, Cocoa or PyOpc as shipped with Leopard and drag and drop that onto 10.whatever, 11.whatever, whatever the next OS is, it should work. And if it doesn't, it's our bug.

So what do we have today? Well, right now, most of your scripting languages out there already do a lot of bridging. And in particular, they bridge to the system layers. So you have all the BSD, BSD-like APIs are wrapped up in the scripting languages. Many of them provide GUI toolkits, WX Python, there's Ruby TK or something, I can't remember which one it is, but there's a bunch of different ones.

The problem though is that they don't support building truly native apps. You build one of these, an app with like WX Python and it looks like lipstick on a pig. It just does not look and feel like a real Cocoa app or a real Carbon app. So, then comes the question of which languages to support. And, well, which language is better? It's not interesting, and since this is only a half day, we don't have the hours to discuss it.

However, what you want, though, is a language with a strong object-oriented theme. And you really want ones for us that already have high-quality bridges because, well, that's less work and we already know the community is adopting and has momentum. And for Leopard, we're committing to Ruby and Python.

So, why Ruby? Well, it's object-oriented from the ground up. It's obviously a very popular language and it's growing. And beyond everything being an object, it also has a very dynamic philosophy of implementation that's close to the Cocoa model. And there's the Ruby Cocoa Bridge, which is excellent and getting much better, and it's been in development since 2001.

Similarly, why Python? Well, Python has a very mature object-oriented model. It has a few warts, but not too bad. Though it also supports functional and procedural, it's a very flexible language. It is designed to be embedded and bridged, etc. And there's lots and lots of modules out there that demonstrate how to do this kind of thing. But there's also the PyOpsy Bridge, which has actually been in development since 1994, came up on Next Step.

Well, it was forged in hell of Wall Street. I don't know how many of you have ever done financial apps, but when you go to a trader and you tell them that, well, you're $150 million, don't know where it is. They get really irritated. So PyOpsy was developed in that kind of an environment and has a real focus on producing the right answer and doing so consistently.

So why not some of these other languages? Well, Perl, we asked a lot of people and there wasn't a lot of interest in developing Cocoa apps with Perl. And there's CamelBones, which is an excellent third-party bridge. So if you want to use Perl, go use CamelBones. There's the Cocoa Java bridge, while it's being deprecated. And is this going to happen to the scripting languages? No. The Cocoa Java bridge was, um... Not the most easily maintained implementation. There was a lot of manual mapping.

There was a lot of trying to make Cocoa feel like Java. And because of that, it was just a really hard bridge to maintain. And because of the impedance mismatch between Java, Java wants really strong typing and it wants to know things up front. And Cocoa Objective-C, which tends to be very late-binding and dynamic, just didn't fit well.

There's also Fscript, which as you saw in the design awards, did quite nicely. Fscript is an incredibly powerful tool, but it's not really for building full-blown apps. It's much more something you might use to embed as a scripting language for your app, or you can use it as an amazing debugging tool. So with that, I'd like to ask Laurent to come up and show a little bit of Ruby Cocoa.

Good morning, everyone. So my name is Laurent Sansonetti. I work for the CodeOS group inside Apple, and I'm here to show you a quick demo of the Ruby bridge. So in the part, we intend to provide a nice integration. We intend to nicely integrate the scripting bridges with the Apple developer tools like Xcode and Interface Builder. And in the seeds you have in hands, there is already some preliminary support for Ruby Cocoa. So let's just open our demo project. So most of the files in this project has been generated by Xcode.

You have this main.m file that you should be used to. This main.m file is mostly the same as you see in regular Cocoa applications. It's just that instead of calling NSApplicationMain, we call RBApplicationMain, which will initialize the Ruby Cocoa runtime environment. This Ruby script will be executed just after the initialization. We load all the Ruby scripts present in the resource directory of your application vendor. And then we call the regular NSApplicationMate. So let's have a quick look at the user interface.

So it's pretty simple in fact. We just have a text field here, here we have a slider, and the main view is from a new framework in Leopard called ImageKit. ImageKit is a new framework, so a meme to be used to browse and display images and stuff like that. And here we're just using the image browser view of this framework. We have here a class that inherits from NSWindowController and we made all the specific connections and stuff like that. The Ruby implementation of this class is pretty simple.

So as you can see, we just inherit from NSWindowController, as we would do in Objective-C. It's pretty trivial. Here we declare the only outlet we have. So this is the image with a view. So this is initialization stuff. Here we have the two actions we have. The first one is for the slider, and the second one will be activated when you enter something in the URL text field. We just parse the contents of the URL, and then we parse using the RSS library from Ruby, in fact, from the Ruby standard library. So we just parse the contents of the given URL, and we keep that in the cache. Oops. Here, oh, but in addition. Oh.

Anyway.

[Transcript missing]

More or less, okay. So this is the data source implementation of the ImageBrowserView. The first method just returns the number of items in the view, and the second one, you need to return an object, For a specific index in the view, this object has to conform to a certain protocol. Here we return an instance of the RSS photo class, and I will come to that later.

So here is the RSS photo class. So this is the magic stuff here. This class is a pure Ruby class. It doesn't iterate for NSObject or stuff like that. So it's a pure Ruby class, and the magic is that we're going to pass instance of that class across the bridge to the ImageKit framework.

And the bridge is going to create proxy objects for each instance of that class and pass proxy objects back to the Objective-C underlying stuff like ImageKit. And ImageKit is going sometimes to call back methods of the proxy object, and the bridge is just going to forward everything back to the Ruby class. So here these are... These are three callbacks that will be called by ImageKit. And here we just returned an NSImage class, an NSImage object based on the contents of the URL. So let's see if it works. There we go.

So I'm just using the Flickr feed for WWDC tag, so all the image tagged with WWDC. And it works. So we can use a slider to zoom, stuff like that. And it's pretty simple. In fact, it's I don't have the number of lines, but I think it's just 18 lines of code, so pretty simple. So as you can see, it's pretty easy to glue existing pure Ruby code with Objective-C frameworks. So in that case, we used the RSS library, part of the Santa Ruby distribution, and we used this new ImageKit framework and Cocoa, of course. Thank you.

Thank you, Laurent. So as Laurent demonstrated, I mean, it feels like building a Cocoa app only with a scripting language. So the state of the bridges. This is kind of where are they, where are we going. So the bridges, they all allow mixing of Objective-C and scripted code. And this really gives you the freedom to prototype and implement in the scripting language using the rapid application development patterns that I mentioned, and then turn around and optimize performance-critical pieces by porting those pieces to Objective-C if necessary.

And you can use these to build production quality apps. There's applications today that ship written with Python against Cocoa that you could buy in a box at the Apple Store, and you'll never know the difference. Same thing with Ruby Cocoa. And the PyOpC bridge at least, and I'm now getting reports about Ruby Cocoa as well, has been used in a lot of research facilities. Lawrence Livermore, Sandia Labs both use it. Trading houses, like I mentioned before, engineering groups, both Pixar and Apple, have used PyOpC quite a bit.

And the unfortunate part about the state of these bridges on Tiger is that to maintain compatibility and make sure that a software update or something else doesn't break you, you have to embed the runtime, the bridge and the language interpreter in your dot app. It's about a, I think, what, six, eight meg penalty, something like that.

And that's really what we're focusing on. We want to fix that. We don't want you to have to drag that garbage along. And we want to make sure that if we ship a software update with, say, a security fix for something, then your app takes advantage of it.

So Cocoa and Objective-C really provide the most natural point for doing this kind of bridging. And that's largely because most of your scripting languages, even if they're not object-oriented, they have a very object-oriented feel about them. You're always dealing with some random thing that you put a dot and then tell it to do something.

And on the other end of the bridge, the Objective-C runtime provides all this metadata at runtime, live metadata, that describes all the APIs on all the classes and the instance variables and everything else. And now with Objective-C 2.0, there's actually a lot more metadata available about things like properties and stuff like that. And this metadata is rich enough that we can handle most of the APIs provided in the frameworks automatically in the bridges themselves.

The one key point and the first thing you run into is method names. How do you map a method name, which has a bunch of colons in it, into a language that doesn't allow colons and names? We choose a very simple rule. This is what PyOpc's been doing for 14 years, 12 years, 12 years, whatever. And that's every colon in the method name, in the selector name, you simply substitute an underscore and you're done.

There's no ambiguity, there's no exceptions. We don't have any magic mapping files, which the Java Bridge did, or special cases. Actually, in Laurent's demo, he demonstrated a version of Ruby Cocoa that has a convenience mapping in it where you can drop the last underscore. That won't be supported out of the box in Leopard because, again, this is about forward compatibility and really ensuring a consistent environment. At the same time, we're not going to actively prevent anyone from adding those kinds of dispatchers in.

So when you talk about methods, there's more to them than just the name. There's also the signature. And the signature-- well, in a scripting language, typically there's no types that are really declared. I mean, sure, there's types when you get under the covers. But you don't say, hey, I'm going to take a string argument and then an integer argument and then an NS table view.

Which is problematic because in Objective-C, method arguments very much have explicit types. Now the bridge, what it generally does is it will try to derive the typing information from the superclass or from context. And what that means is that if you override a method in subclasses, because we support subclassing in both bridges, then the fact that that method takes an integer and an ID will be grabbed from the superclass if at all possible. Or if it's a delegate method or protocol, it'll grab it from that.

The problem though is that that's not always enough. So this is the one area where things on the script side of the bridge get a little different. The bridges provide hints for specifying this additional metadata. In the Python bridge, PyOpc, you have @ decorators. We're using the decorators from Python 2.4. So if you say @ typed accessor with an i, that's going to make a method on the Objective-C side that takes an integer as an argument, and it's a true integer. Or it's going to return an integer if you wanted to just have quantity.

At class method is used to declare class methods on the Python side, because Python doesn't have class methods. Likewise in Ruby, Ruby has the, can't remember the name, these decorators for the declarations. So you can say flag auto update enabled is a KVC/KVO compatible attribute of this class. So now there are still some obstacles, because really Objective-C is just ANSI C with a bunch of extensions. And the C bits are really hard. Effectively, C is a macro assembler, so you get to deal with grunging through memory.

Yes, yay, except for when it bites you. So things like NSErrorPointerPointer. Well, that in and of itself isn't that hard to handle, except for the fact that in some cases it goes out. Some cases, the things like that go in. Sometimes they go in and out. And there's not a consistent amount of metadata in the runtime to know that.

Likewise, you have sometimes paired arguments where one argument tells you how to properly deal with the other one. If you have, like, say, a float pointer with a count and you run off the end of that or make a guess, your program's going to blow up. Memory management. So Objective-C has retained release, auto-release. The scripting languages are typically garbage collected. Python has reference counting. Ruby has a garbage collector. And that can be a challenge.

And so are exceptions. Every language seems to feel the need to invent a new exception model. And as we cross the bridge, what we actually do is convert the exceptions between the two languages. So if you have an NSException raised in Objective-C and it passes through Ruby, it's going to be a Ruby exception. Then as it goes back into Objective-C, it'll be turned back into the same NSException as before.

On the memory management front, conveniently reference counting is a natural match. So the bridges actually take care of all the rest of the stuff. So it's a nice little feature that you can actually take care of all the retained release, auto-release for you. Frankly, programming in Python against Objective-C or Ruby against Objective-C feels a lot like using Objective-C 2.0.

There's also other kinds of arguments that the metadata won't describe. And of course, what about garbage collection? Well, neither bridge uses the garbage collector at this time. We're investigating ways of integrating that. But frankly, because the bridge has managed routine release, auto-release for you, when you're on the scripting language side of the bridge, you really don't miss it. When you're on the other side of the bridge, of course, writing your own code, you will need to deal with retain, release, auto-release for now.

So clearing out these obstacles, what we're adding is a big chunk of metadata to describe all these difficult bits. And the metadata will be loaded by the bridges at runtime. So we're moving away from some of the static wrapping methodologies that were used in PyOpc and moving to an automatic system that will allow the metadata to describe, for example, which methods return a Boolean versus a character, which is critical to Ruby and very convenient for Python. It'll also include other information, because there's a bunch of symbols in your frameworks that don't get exported, like your sharp defines, your type def and nums.

Functions, they'll be exported as a symbol, but they don't have a signature for knowing what types of arguments they take. Sharp defines, this is again going to be read when your script or your application starts up. We are, of course, looking to make that as fast as possible.

So there's some additional features to these bridges. There's native types in all of the languages, Objective-C, in Python and in Ruby, that really are the same basic underlying data structure, even if they have a little bit different APIs. And to really make the languages usable, you want the Objective-C types to fit well with that. So the bridges do a no-cost or a very low-cost sort of bridging-like core foundation to the foundation.

And what this results in is that you can write code like this, where you're saying, "For some bundle..." I see my "for" was capitalized. Don't do that. So, "For a bundle in NSFramework.allFrameworks." That will enable you to do that. So you can enumerate the array, the NSArray returned by all frameworks, and the second line is just going to print out the bundle path of each one. Likewise in Ruby, using the block syntax, there's enumeration of arrays. And this extends also for some other types.

nd we'll talk about the key value coding and observing. Supporting key value coding and observing is critical to making a high-fidelity Cocoa development experience. It's the only way that Cocoa bindings will work, for example. Both of the bridges support automatic key value coding and observing, and they do so at assignment as opposed to having to call the methods, so again, very Objective-C 2.0-like.

These lines of code are all equivalent, so if you just do myManagedObject.someAttribute = something, that's going to trigger KVC/KVO. Likewise, KVO is automatic, so even in a non-managed object, making an assignment to an instance variable is going to cause the KVO notifications to fire. This actually greatly reduces the number of lines of code you end up writing too.

So, there's also declaring instance variables. You saw earlier how Ruby you could declare an instance variable to be a KVC variable. In PyOpc Python, there's a function on the Opc module which allows you to create an instance variable, and this actually, when the class is dynamically created, will create a slot for a float named count. And this will behave the same as using a typed accessor, if that's the way you want to go.

Of course, a lot fewer lines of code. So performance. You know, one of the things that always comes up whenever I've wanted to use a bridge on a project is someone starts whining about the performance. And the reality is that in most of our apps, Cocoa is what's really doing the heavy lifting for us.

Your code generally is not going to be the one that's being executed all the time when you're doing like Core Data filling in an S table view. Now, there is of course going to be random algorithms or whatever other performance-intensive code you might write, and you can easily port that to Objective-C as needed, with the caveat being of course that you did the performance analysis first to make sure you're not wasting your time.

There is a cost associated with crossing the bridge. Like I said before, exception bridging, it exists, it happens. It's expensive simply because setting up and tearing down Objective-C exception handling blocks is expensive. The argument return values are boxed or converted, meaning that you can't just pass a random integer into, say, Python because it's gonna blow up. So it needs to be turned into a Python integer or a Ruby integer, et cetera.

And you really just want to avoid tight performance-sensitive loops that are constantly crossing the bridge. If you just kind of keep that in mind, I don't think you're going to have a lot of problems with performance on these. It just really isn't that big of a deal. So I wanted to do a quick tour of PyEbc.

So this is actually a project that I started not terribly long ago. We have the dedicated network builds, and we're playing with visualization ideas. And I'm going to run it just so you can see the god-awful hideous colors that I have right now for testing purposes. So you can see it's doing stuff, yay.

But what this is doing under the covers is actually pretty interesting, because it's listening on a UDP port, waiting for notifications to come in from random machines. Then it's taking that data, tearing it apart into a dictionary, throwing the dictionary over the wall to a Core Data in-memory store, saving everything in that, which then triggers a context-did-save notification, which gets listened to, which then updates the view.

So the reason why I actually did this as a PyOpc app is because I could take advantage of Twisted. Twisted is a third-party network application development environment. It's like the AppKit for network apps. It's amazing. And it saved me a huge amount of code. I don't know how many of you have ever done network programming, but dealing with TCP sockets and UDP datagrams is usually a real pain. But this right here is all the code it took to set that listener up. And this is using a subclass of a visualization protocol. This is the entire code for... Well, here, let me just delete the code we don't need.

Yeah, my tab width is hosed, so I'm not going to edit anything or else it blows up. Yeah, here's a hint. You saw it in Laurent's demo. You've seen it now in mine. Turn off tabs in Xcode before you start dealing with scripting languages. And somebody please file a bug against that, because I need to make that a default. That's the biggest source of problem with all this.

So, you know, this is really hard in C. This is hard in CF socket, but you see how much code I've reduced by using this. And then you'll see in my project, I'm mixing and matching Objective-C and Python at whim. For example, I've got one managed object as an Objective-C object, another managed object as a Python object. My app delegate is a random Python class. And let me show you a couple of interesting features in here.

This right here What's actually happening is when Python comes up, because we've got this wonderful dynamic runtime that likes to compose things on the fly, I'm going and I'm reading the specification of the class AppDelegate from the nib file. It's figuring out the superclass, the outlets, the actions, and then Objective-C or PyOpC, using this auto-base class notation, is actually defining that class on the fly with all the appropriate outlets and instance variables. So if I go into Interface Builder and I make a change there, I don't have to sync anything in my code. It just works.

You'll also see things like this where, you know, on the one hand, I wanted to put a debugging thing in here, fake it, man, and I wanted to run an external program. Well, the fastest way to check for a program is to use this, but the fastest way to launch one was to use nstask. So you really get to mix and match whatever tools you need to get your job done. Let's see. This is interesting just in that if we go down here, yes, this line right here.

This code here. So whenever a machine notifies my computer and it goes off and it says, hey, I've just started a job, I need to create a new job entity in a managed object context. And it's just a matter of doing the typical Objective-C thing of this. I'll admit an entity, shove it in a managed object context, but then I can turn around and do basic assignments to do all the setting of the attributes.

And I can use this little chunk of code to do basically components joined by string, like on an NSString, just a lot less code. You'll also see things like, you know, mutable set value for key, all the proxying there works correctly, everything else. So in cases where there's an NSError pointer pointer, what PyOpc actually does is it returns the error as a tuple. So you get the return value and the error as one return value. There's a notation so you can have it not generate the error at all as an optimization if necessary.

Things like perform selector, you know, it just works. And that's the whole point of this. And, you know, I estimate that this, using this really probably shaved about half my development time off this and freed me up to really experiment with it, as you can see in my color scheme. So back to slides, please.

So Apple has really embraced the Ruby, Cocoa and the PyOpc projects to make Ruby and Python first-class development languages for Cocoa. And we are working very closely with the community. The primary developers are actually here in the audience today. And I wanted to get through this fairly quickly because I imagine there's going to be a handful of questions.

We've already posted some changes back. The changes to the PyOpC bridge are being hosted in a private repository right now until we can get permission to open it up a bit. The Ruby Cocoa changes have actually been changes that haven't really been, you know, in violation of the Leopard NDA. So you've seen a lot of activity in the public community on that front.

On the Leopard DVD, most of the batteries are there. For Ruby Cocoa, the bridge, the interpreter is included. You get the Xcode templates, there's some examples, there's documentation. For PyObc, we got the bridge and the interpreter in, but the templates, examples and documentation is not included yet, just because of the way the project was structured. It's being cleaned up. However, you can find all those online, and they work fine on Leopard. That project I just showed there was just a standard Xcode PyObc project template that I instantiated from the, grabbing the template from the website.

And for more information, you've got pyobc.sourceforge.net, you've got the Ruby Cocoa Source Forge Project, you've got rubycocoa.com, which is awesome. It has a huge write-up of Ruby Cocoa and sort of the philosophy of implementation behind it, history of Next Step and AppKit and all kinds of great information, including even some Pyobc stuff.