Mac OS X Essentials • 54:46
Learn how you can write first-class Cocoa applications in Python or Ruby. With better meta-data driven bridging bundled in Leopard, you can use PyObjC and RubyCocoa to leverage the power of Cocoa from your favorite scripting language. Discover how you can write dynamic applications faster and easier than ever before.
Speaker: Bill Bumgarner
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
I'm Bill Bumgarner. I'm manager of Objective-C garbage collection and generally wrangle the wizards that do the runtime. And we've got some pretty cool stuff to cover this morning. In particular, we're going to look a little bit at the history of bridging technologies, how we can get to Objective-C from some other technologies and show you exactly what's in Leopard. The map of the components and the content within Leopard.
We're going to focus on metadata and I'm not going to go into that right now but you'll see that this is the underpinnings of everything we're doing and we're going to look in detail at bridging technologies both how the bridges that we have in Leopard are implemented as well as how you can build your own.
Then we'll look at some high level support and then we'll wrap up with a little bit of errata, some gnarly unfortunate details. And then hopefully have some time for QA. So let's dive in. So bridges have existed between Objective-C and other languages for a long time. More than two decades.
Objective-C is a very convenient language to use because it's so flexible so of course people have bent it to all kinds of different things. And in Leopard, what we're doing is integrating a number of these bridges and providing support for additional languages and infrastructure. So when we talk about a language bridge, what are we really talking about? Well there's a couple of different kind of levels of bridging.
And really it's about enabling one language to execute code from another language. It's not translation, it's literally taking one language and letting it talk to another. They can be one way and still be useful. You commonly see this in applications that have embedded scripting languages of their own.
You know, go tell the app to do this and the app goes and does that and doesn't really ever tell anything back. They're often very restrictive by design but that's a feature if you look at Lua and what it's done with World of Warcraft and the fact that entire user interfaces can be built in this network to massively multi-player online game without cheating. That requires a lot of restrictions.
So good bridges, and we're really going to focus on Objective-C because, well, that's kind of at the core of Mac OS X. Good bridges allow one programming language to talk to another. That's about it. It's powerful but it's pretty limited. Moving on a better bridge actually allows the data to flow between the two languages and there's some subtlety there which we'll get into in a minute.
Finally the best quality bridges, when we talk about Mac OS X and we talk about Objective-C are ones that allow subclassing of Objective-C classes with full fidelity. And that means support for the idioms provided by the foundation by the Objective-C run time, the AppKit, Core Data, everything else.
Key value observing, key value coding, all these other patterns, and provide access to the pure C APIs that underly the Objective-C. So Python and Ruby from the open source communities have provided us with best quality bridges for quite some time. And we have adopted and included both of these into Leopard.
So why Python and Ruby? Well, they're object models, they are both object-oriented languages and they're object models pretty close to Objective-C, unlike Java. Likewise both of these languages are designed to be embedded in other applications. Again that's very different than Java. The Java virtual machine really wants to be an operating system into and of itself. It wants things embedded in it. It doesn't like being embedded inside say a dot app.
There's a large demand for better Mac OS X-specific support in these languages. We see these requests all the time. Access to more of the Mac-specific APIs from the languages. And we see the requests all the time for people to write user facing that is desktop applications, double-clickable, drag-and-droppable applications.
Plus these languages have huge and growing communities. One of the things, I'm friends with some of the people at O'Reilly and they publish their book numbers and it's kind of interesting to see that Python and Ruby are the two book markets that are growing year over year consistently in these languages. Whereas some of the other languages are kind of stagnant if not falling off. But that's books. When we talk about code there's huge amounts of reusable open source code available for you to recycle.
As well, there's often kind of research and development innovative front-line solutions that are becoming available first in these languages because the productivity of using them is so high. And there's already shipping applications or soon to be shipping. In Leopard we see the Calendar Server which is using Python through PyObjC and we also see the Podcast Producer which is using Ruby and RubyCocoa.
So when we look back a little bit, look at these bridges. There is some history here that's kind of interesting. PyObjC was originally created in 1993, it was actually a part of Python that got thrown out about the same time NeXT killed their hardware business. Subclassing wasn't added until 2002 which I me ntioned as being interesting just to emphasize that if you're thinking about writing your own bridge, subclassing is kind of hard but you don't have to get there and still have something very powerful.
This is used in commercial apps and vertical markets all the time. There's trading companies on Wall Street and on the Chicago Boards that are maintaining billion-dollar positions using Python through PyObjC to Objective-C. RubyCocoa, a little bit newer bridge, started in 2001. Really took off last year, gained tremendous amounts of performance, much better quality bridging, it's really production quality now, great stuff. And it was actually the sandbox for a lot of the technology we'll talk about today. There's been some other bridges, there was a Tcl bridge, CamelBones is an active development, there was a Cobol bridge, Objective Cobol, don't see much interest in that.
There's a Lua bridge, there's at least two C# bridges, there's at least one commercial app shipping with a C# Objective-C bridge from Mac OS X. So really these things do work very, very well. And I'd like to ask the Checkout folks to come up and demonstrate exactly how well they work.
( Applause )
Thanks, Bill.
I'm Koen. I'm the head developer with the Dirk of Checkout. We are an Amsterdam-based company, Sofa, and we, and down there are Jasper and Hugo and they provide all the cool Cocoa goodness, the beautiful artwork we use in Checkout all the time. We started Checkout about two years ago. I was doing a lot of veejay work with Hugo in big clubs, and we were using this program which was called Modulate. And it shipped with a Python scripting language to influence all the interface features of it.
And we started to look into it and we really, really liked it. And at the same moment I was working with Dirk at a big Apple reseller in Amsterdam and they were using a very, very bad point-of-sale system. And we looked around for a better solution and there wasn't any. And it wasn't really an exception that theirs was so bad.
So we decided to try to roll around. So combining the knowledge we had about Python and the PyObjC bridge which we discovered we did that. And that turned out to be a great decision for us because we have seen like two big advantages of using Python for point-of-sale app. The first one is that it is a scripting language so you can develop very fast.
If you read on the Internet about developing in Python, a lot of people use it for developing prototypes but with these fast Intel Macs and the stability of Python on the Mac and the PyObjC bridge, you can actually ship that prototype as a full-blown app to your customers and they won't even know that it's a scripting language.
The other big advantages is that there are lots of major libraries for Python. So when we started Checkout two years ago, Core Data, I don't even know if it existed, but we heard from it. But we actually wanted to roll a network- enabled version of a point-of-sale system.
So at that moment we looked around and we discovered a great object library which was called SQLObjects and we built our app around it. And that worked really well for us. And the next version of Checkout is actually going to be built on another object relational mapper which is called SQLAlchemy.
Together with PG Boss to control a Postgres server that shares and advertises over Bonjour and users can log in and it's really fun to see that you have all these cool frameworks you can choose from and you can incorporate great functionality within just a few hours sometimes. So we're really excited that Apple is treating Python programmers now as a first-class citizen on the Mac platform and Dirk is going to do a small demo of our app.
Thanks, Koen. Okay guys, if you look at this window you wouldn't really know it's not Objective-C. I'm going to start with the example store. If you look here, this is all Cocoa code. This is our own and a split fuse subclass down here. And another one down here if I can reach it to save the feature. So basically I'll show you a little bit of how the app works and then we'll point out a couple of cool things we were able to do thanks to all the Python frameworks that are out there.
So normal users would be, I create an order, direct some stuff in or double click it or do my instant search here, I allocate it, I invoice it, oh, need a serial number for that product, and I pay it. And as you can see these are all native Cocoa interface elements.
And some of the cool stuff we have been able to do thanks to Python is, for instance, we have our our own take on bookkeeping, we try to shell out you know that bookkeeping is fun and you should make your accountant happy. We're hoping eventually if we say that, if we repeat that long enough, it will eventually become so but we're not quite there yet. This export button for instance, it took us more time to layout you know what this should look like than to actually implement the native Excel file support. So I'll just save document here.
There it is. And how does that quick look thing work again? Oh it's not in Excel, mime type. Well, just trust me or download the app yourself, you'll see it just exports plain simple Excel workbooks and it was just an hour to implement or something. Some other cool stuff we do also in a manager interface is we leverage WebKit. So we really get the best of both worlds, you know? We can use open source Python frameworks where available, where they're best and we can use, you know Apple's Cocoa stuff wherever that works best.
So I think, yeah, that's it and if you want to see, test for yourself if it really works like a Cocoa app, how fast it is, download it yourself, take a look. It's available at Checkoutapp.com and you'll see that it's really, you know, it's just that we're here that you know it's a Python app. It's 17,000 lines of code.
- Thank you.
- Thanks, Bill.
( Applause )
Let's go to the slides, please. Thank you. So that's pretty cool. That's a Cocoa app. I mean you know people sitting down and using that application don't realize that that's a Cocoa app written in Python backed by somewhere around twenty thousand, a little less, lines of Python code. You know, a beautiful look and feel and as they said moving into version two they're able to leverage all that power in the Python open source community to achieve a velocity of development that their competitors using some other traditional database application development environments simply cannot meet.
So as you've seen the bridge works. I mean that's an application shipping on Tiger today, drag-and-drop install works great. The languages, they're easy to learn, the bridges are easy to use, they're highly productive, powerful, and popular. We see lots of activity in both the RubyCocoa and the PyObjC community.
There's lots of marketing buzz which ultimately you know we've got to line our pocketbooks somehow. And choosing popular technologies is a good way to do that. However there's an added benefit: Lots of cutting-edge, cool front-line code out there. You know, the whole Web 2.0 thing is all scripting languages.
Got the shrink-wrap apps already shipping, there's some other apps on your system that you may not know have the bridges in them. Adobe Lightroom, for example, has an Objective-C bridge in it from Lua. Talking Panda, the guys that do the really cool little bartending and language translation dictionaries for the iPod, they're installers in Python. There's lots of vertical markets, some of our missile defense researchers in the United States are using Python and PyObjC and RubyCocoa.
So excellent for prototyping and exploration. You can grab these foreign tools, you know we mentioned a couple of them but there's also, there's like Twisted which is this amazing, it's like the app kit for network programming for Python. It's just amazing. It can be integrated straight into Cocoa apps so that network events show up in the event queue just like mouse events. There's Rails from the Ruby community which is a huge number of different very flexible packages and cutting-edge technologies that you can easily integrate.
So if all these things exist why are we changing them in Leopard? Well, so all the bridges using Objective-C runtime metadata. But when that metadata was put together twenty plus years ago, it wasn't complete. There's not enough information there to do full fidelity bridging. In particular you end up having a lot of hard wired special sauce. You got all these things like methods that return Booleans which the Objective-C runtime says is a character. Ruby doesn't like that.
You've got things like NSErrorPointer pointers. Is that going in? Going out? In, out? Who knows? Things like static inlines. These only show up a compiled time along with sharp defines. Critical pieces of information for your development experience. Not available outside of the compiled environment. There's also things like the external references. Symbolic references that, global variables that are very critical to controlling Cocoa.
And the bridges also tend to expose everything kind of equally so you'll have things like private API, internal API, stuff that was researched that just got left in the binary, should never be touched. It's all bubbled up at the same kind of level. And we've seen cases in the, like the PyObjC mailing list where someone goes off and they call some random method and everything blows up, and like, oh, why doesn't that work? Well, that method is not supposed to exist.
And a lot of the stuff has to be reinvented across each bridge. New languages start from scratch or they start by downloading PyObjC or RubyCocoa as the two most mature bridges and then they go through all the same trials and tribulations that these bridges have already gone through. So fixing all of this is critical if we want to build an environment where we can support Cocoa developers in these scripting languages for the long term as well as building a foundation for further integration with Objective-C.
So the solution to this is what we call metadata-driven bridging. That is to provide metadata like the Objective-C runtime but externally that describes everything needed to do this level of integration. It needs to be machine parseable but human readable is preferable as well. More than just the Objective-C APIs is required, you've got to get all that other detailed stuff that gets passed through that Objective-C APIs if you really want to have full fidelity bridging.
It needs to be included with the system frameworks, this is something that because scripting languages are so dynamic interpreted on the fly on the user system, we can't require development tools. And you've got to cover the pure C API's because there's a tremendous number of very important pure C APIs in the system, FSEvents, things like that.
It has to include constants, #defines, type def enums, structures, definitions, functions, class-specific exceptions, inlines, you name it. Which means it also needs to have some mechanism via which you can dynamically load a little bit of glue if required. And we've done that, exactly that. And actually it was developed in the BridgeSupport project as a part of RubyCocoa and it's available in MacOSXForge.
It works on Tiger as kind of a preview state, none of the metadata is included with it but you can go generate it yourself. And the RubyCocoa bridge is using that on Tiger as well. So we've made some promises at WWDC 2006. We said we'd have Ruby and Python support, we'd have some limited Xcode support, limited framework support, and we'd have some metadata to describe the frameworks we were going to support which was AppKit, Core Data, Foundation.
We're delivering in the seed that you got this week as well as in Leopard the Ruby and Python support. Solid Xcode support. It's not complete but it's much better. You'll see syntax coloring and code sense. Lots of frameworks and APIs are supported with metadatas and tools to support non-Apple. And by lots of frameworks, I mean, lots of frameworks. So.
( Applause )
- We pretty much have metadata generated for most of the APIs on the system. There's some C++ APIs that, trying to describe that is difficult. And we also have tools such that you can easily provide metadata for your own code.
( Applause )
So Perl, the CamelBones bridge, that's under active development in the community. F-Script anywhere is going to be using BridgeSupport in Leopard.
( Applause )
Yeah, they won an Apple design award last year if I remember correctly. There's full metadata, so it's not just the metadata that's missing from the Objective-C runtime but it's actually the full metadata that's described in these files, which we'll show you in a second. Which means this stuff can be used well beyond just bridging languages. You now have a machine-readable description of a lot of the APIs in the system and the ability to generate that description for more.
There's mechanisms for generating your metadata for the new APIs that can be integrated into Xcode's build system. So it's easy to add a build phase, it generates the stuff on the fly if you want. It's fully documented, including an XML DTD. It is XML. And there's full 64-bit support so the underpinnings lib FFI as well as the bridges have been supported to 64-bit. You've even had some testing.
So when we look at Leopard, this is what the map of how the stuff fits together looks like. So you've got the Python and the Ruby bridges included. Perl, F-Script, Lua are coming from third parties, the double arrows are supposed to mean metadata but it turns out by the time between when this slide was produced and now F-Script and Lua have started using the bridge supporters, starting investigating that.
So you know, this community moves quickly. There's another technology in Leopard called the ScriptingBridge which I'll talk about in a minute, that's a bridge to Apple events from Objective-C which plays into this quite nicely. And then, of course, Objective-C is what most of the system frameworks are implemented in.
So the ScriptingBridge I want to talk about that for just a second because it kind of completes the picture here. The ScriptingBridge, if you went to the session that covered it, I think it was the App Scriptability session, it's a bridge, the bridge is between Objective-C and Apple events. Basically it lets you treat any application on your system that's scriptable like it's an Objective-C object.
So you can statically generate Objective-C code, like for example, this chunk of code is almost all of what you would need to play a random track from iTunes, treat iTunes like an Objective-C object, talk to it, play a track. Which means you can use it from anything to bridge to Objective-C. It can be used from any language it's already bridged to Objective-C.
Or the ScriptingBridge can now dynamically generate classes at runtime which means you can do the bridging without requiring any compilation. So it means you can drop into the Python interpreter or into the Ruby interpreter and talk with applications on your system on a user system, no dev tools.
( Applause )
( Laughter )
So let me go ahead and bring up the Ruby interpreter and I've got a little cheat app so you don't have to watch me type and correct my mistakes. I'm going to go ahead and import the different components required to bring up the RubyCocoa bridge including a couple of extra lines that I see Laurent not liking.
But it's actually simpler than this. And then once I've got that up, I've brought in the ScriptingBridge framework, now I'm going to create the class object for iTunes. We've actually dynamically generated an Objective-C class for the iTunes application and now the RubyCocoa bridge has created a proxy to that class sitting in the Ruby runtime. And we've actually created an instance of the iTunes class.
So once we've got that, let's go and dump the contents of my library and this is where my manager cringes. So it's very safe though. So you can see what we've actually done here is messaged into iTunes, grabbed a list of all the sources, grabbed all the playlists, dumped all the contents, etc. If I had sound on this machine and the RA double A approval to play this stuff I could play it and control the volume, etc. So that's pretty cool.
So one of the other features of the bridges is that they, the Python and the Ruby bridges try to actually meld the object models of the two languages. So and we'll get into this in a little more detail in a moment. So what this means though is let's bring up Python and what we can do here, is we'll go ahead and bring in the Foundation.
And once we get the Foundation in then we will create a subclass, pretty straightforward, we're just creating a subclass and then this object overrides the description method so when I start messaging it you can tell it's something other than an NSObject. We'll create a couple of objects, in this case we're instantiating one instance of our foo class, we're instantiating another instance of NSObject, make my font a little bigger, then we will go ahead and print out the descriptions so you can see standard Objective-C messaging works, doesn't matter if it's the Python subclass or standard Objective-C. Let's go ahead and define a function, a simple function, takes one argument that doesn't use it and just returns a string called howdy.
But let's do something interesting with it. I'm assigning a function to NSObject.description. Which means that what I've actually done is replace the implementation of Objective-C method with a pure Python function. So that's...
( Applause )
Then go back to slides please. So that's the level of intimacy of bridging that we're talking about here in best-of-breed bridging. So let's look at this code again, just briefly here.
It's kind of like Objective-C in a lot of ways. You see things like, okay, we've got to go and we've got to grab the different interfaces for what we need to use, kind of like a pound import only we don't have a link phase so this is also linking it effectively into our runtime.
Then we need to figure out what we're going to go and talk to. So in this case we're getting classForApplicationWithBundleIdentifier which is a ScriptingBridge thing to generate that class. Pretty straightforward. And then you'll see here we're doing some Ruby construction you'll see that we're actually like if you say app.sources.each that's actually an Objective-C method followed by a Ruby method.
And then followed by Ruby block. We're looping through and printing some stuff out. Similarly on the Python example there, include in link. Define a class. Declare a method. Print some stuff out, return the string, etc. You see here this is another example of where, even though we have an Objective-C class, that actually supports single inheritance because we're in Python that does have limited support for multiple inheritance. Our super invocation reflects that.
So we've got kind of the best of both worlds there. Creating instances, both the bridges, you don't really have to do retain, release, autorelease because the underlying languages are ref counted. It's kind of like garbage collection but not really. But generally you just don't have to think about it.
Invoking methods. Sure, that just works. We can define functions, of course, and then we can plug those functions in, in Python. Python treats methods as first-class objects. So you can edit class implementations by simply replacing the method objects in the class implementation. That has been proxied to NSObject. There's some gnarly code under the hood that takes care of this but it's all public API. My team insisted I say that.
Then we can, of course, message those and you'll see you know just like any other Objective-C thing you override a method and sure enough you get the new implementation. So when we're talking about bridging the good, better, best model actually works very, very well. A good bridge, let you find that target, let you import that library, link against it, bring it into your runtime. And then allows you to message it. In this case we're going and we're grabbing the main bundle.
And then allows you to call a method. Now this is important here. As you see I've got a method that takes multiple arguments. And I didn't put up both the Python and the Ruby code here because it's effectively the same. I mean there's some idioms for shortening this in either language which I'll show you in a second.
But pathsForResourcesOfType_inDirectory, that's a method on NSBundle. Takes two arguments. Normally in the Objective-C syntax it would all be nicely interleaved with the arguments in the method name, etc. In the scripting languages, do we have one simple rule for invoking methods? Because the scripting languages have kind of a functional invocation.
You take the method name, everywhere there's a colon substitute an underbar including that last one. There's no ambiguity, there's no exceptions, there's no mapping magic which is what the Java bridge did and it ended up with an API that didn't look like Cocoa or Java. And there's no special cases needed. One simple rule and you can talk to any Objective-C object.
Better bridges though allow sharing of data with Objective-C. In particular, when we talk about languages it's pretty much useless to talk about the language from a getting-things-done perspective and not talk about the libraries you're going to be using. And when you use Python or when you use Objective-C it's pretty much impossible to write a piece of code that doesn't have at least one NSArray and one NSString or one string and one array or whatever. You just don't think that way. And every language has this same set of objects: the strings, the arrays, the dictionaries, the things like that, that are almost the same. Except for when they aren't for implementation details the language.
Conversion across a bridge of these types is tedious, it's repetitive, and it's extremely error prone. It's very difficult to get the impedance match correct between these various languages. So the bridges convert the types they can automatically, mostly correctly. There's a few situations where it can't be done quite automatically and this is an example of one. This little body of code, I'm not going to go into too much detail on it, but it's actually has a bunch of interesting things that it's demonstrating.
For one you see we're going and we're getting the pathsForResourcesOfType_inDirectory but we have to do through so this weird PyObjC underscore class methods thing, Python doesn't have class methods, Objective-C does, so we bridge it that way. Once we get that array back, we're using actually standard Python enumeration to enumerate an NSArray. So now NSArrays and Python arrays are interchangeable. Same thing you saw earlier with RubyCocoa.
We can then go and read the data just by simply using that little line of Python, to get the file, read it, that gets us a Python string object back because Python strings can have arbitrary hunks of data in them. Which is why we need to convert it to a buffer. Which is just a bag of bytes.
And the bridge actually knows the difference between the string and a bag of bytes and if we throw the bag of bytes at NSImage.initWithData, we get an image object. And then we can do some printouts and stuff about the image object and you'll see in the bestRepresentationForDevice, the second to last code there with the line of code, we're passing, it wants an NSDictionary.
But we're actually just creating an inline Python dictionary. There's actually two interesting things going on there. One, so we've got a Python dictionary interchangeable with an NSDictionary, that's pretty cool, the second thing is that number two the bridge automatically sees that and turns it into the appropriate NS number.
So we've got bridging of scaler types too in the context where they need it. So end result, you can pretty fluidly write code without having to consider this stuff too much and the bridges will just deal with it. Now the best one though is when you can start doing subclassing.
Both the Python and the Ruby bridges support subclassing. Use a typical declarations for a subclass of an NSObject. They, of course, support declaration of instance variables and because in Objective-C unlike Python and Ruby doesn't treat everything as a dictionary, in Objective-C the slots of the instance variables is very important so there's mechanisms for declaring classes such that the layout information passes through to Objective-C. Both of the bridges support category-like functionality meaning you can inject new method implementations into the runtime as you saw me do earlier. I would generally recommend against not doing what I did there. Don't replace the existing implementations, add to them.
And with Python there is limited multiple inheritance support. Which means you can actually mix in classes into Objective-C subclasses from the Python side. So when we talk about the BridgeSupport lets dive into the metadata a little bit in detail here and we'll get to the actual metadata files in a second.
The metadata supports 64-bit annotations so you can generate metadata that describes 32-bit and describes a 64-bit and outlines the differences between them. There are Endianness-specific annotations. So first thing and last, LSB, MSB, all that stuff. You can describe that in the APIs and the differences between the platforms. There's very little of that but some of it does leak through.
There is a full DTD provided and documentation. You'll find the DTD in /System/Library/DTDs. There's a BridgeSupport man page in section five, it's quite detailed. And there is a gen_bridge_metadata tool. This is a Ruby script that you'll find in user bin I believe. It generates data for APIs as well as generating dialups. It can generate full metadata and it can have exceptions which means you can provide a chunk of XML that will override the automatically generated metadata in case you need additional descriptions that cannot be automatically generated.
And that has full documentation as well. So when we look at the metadata XML it's as human readable as any XML. That's what it normally kind of looks like at first glance. So let's look at this in detail though. So when you're describing your API, you can declare that your API depends on some other sets of APIs. So if you're subclassing something you know you can say, hey, I'm subclassing something for Foundation so I depend on the Foundation. Again, this is all automatically generated content here but knowing about this is important if you do need to do exceptions.
It also describes structures. So in this case we have an NSPoint description which in the typically readable style of XML has the quote marks encoded. Sorry. And there's a 64-bit type versus a 32-bit type. So that's demonstrated here. This shows that NSPoint is just a structure that contains an X and a Y element. This is what allows you to save P.X and P.Y in the Ruby or Python side of the bridges.
There's things called opaque pointers. These are references to things that really do not need to ever be exploded on the Python or Ruby sides of the bridges. These are provided so that there's a way for the bridge to know the type of the arguments without having to dive into them.
There's constants, so in this case this describes an NSInvalidArgumentException that global variable. So as the type isn't an object of some type and gives the bridge all the detail it needs to go look up the symbol and provide a proxy to that on the Python or Ruby side or whatever bridge you want to build.
Functions can be bridged in this case this is the NSAllocateCollectable function. And it has a 32-bit and a 64-bit declaration and you can see the type differences there. This gives the bridges the ability to go and get that symbol and then figure out how to encode the arguments in and out of the function or code the arguments as it goes in, decode the return values as it comes out.
This is a static inline NSMakeSize. Argument type definitions again, differences between 64- and 32-bit. As well as the fact as the return value changes between the two, between 32-bit and 64-bit. So what this will do is that the bridge will actually, when it finds one of these static inlines in the code, it will create a little C stub that can make the call to that, that is not static and not inline so that it can be dynamically loaded and the bridge can do it's normal bridging magic to get to it. NSIntegerMax, here's another type def, this is a type def enum. So it's a constant that goes across. As you can see the 64-bit one's a bit bigger.
Here we have an informal_protocol. Now this is pretty interesting so NSKeyValueCoding is actually an informal_protocol declared on NSObject. And the bridges bring that through as this informal protocol like this so that they have the method signature, all that typing information that's needed on the Python or Ruby sides of the bridge are on the bridge side.
So that by default everything wants to be an object and by providing this information it actually provides two advantages: one it just makes it just work but two it actually means that you now have this metadata that you can say, oh, NSKeyValueCoding. Okay, I need to do that, that means I need to implement these methods.
So this actually provides for potential, for additional validation above and beyond what's possible in the compiler. And then we have class annotations. There's a slew of different ones. This happens to be a method annotation which is interesting for a number of reasons. One it's class_method so it's marked as such.
So in languages that don't support class_methods like Python. Python kind of does but no one does it. You can actually carry that information through. But there's another interesting bit of annotation here and this shows sort of the intimacy of the bridging in between these languages. In Python and Ruby, it's pretty much impossible to have a collection of stuff, collection of objects or whatever without also knowing the length of it. Now on the C side, in Objective-C if you're familiar with this API, it takes two var arg parameters and account.
So there's no way for the C side to know that but we can actually declare in the bridge metadata that this is a method for which the length of the collections of the first two arguments must either match or must be used for the final argument. Thus we have automatic validation of things like buffer sizes and array elements and matching in parody like that.
Finally, this is a class annotation that's actually showing the save method on NSManagedObjectContext. And this is a method for which the argument that's returned or I'm sorry the argument that's taken as an NSErrorPointer pointer, we're saying that arguments out only which allows the bridge to know that it shouldn't try to pass something into there and that it can actually interpret the return value as an object. So this is one of those annoying, you know, NSErrorPointer pointer arguments. And as well the return value is a Boolean, not just a character, so that in Ruby you don't have to put the question mark on the end of everything.
So that's the XML in detail. So now that we've gotten all that Foundation stuff out of the way, what is developing Cocoa with Ruby and Python... What is that experience like? Well, there's Xcode templates provided for Ruby and for Python Cocoa applications. On the Ruby side there's both Cocoa applications, Cocoa document-based applications, Core Data applications and Core Data document-based apps.
Python side there's a Python Cocoa, Python multi-doc Cocoa, if you ask me in the lab I've got the Core Data template and there will be a document-based Core Data template as well. I just didn't have time to get that in. They are full-blown application templates; you can mix and match Objective-C and the language of choice in them. I would be interested if anyone tries to build one that does both Python and Ruby at the same time.
You can also use the new organizer in Xcode as well. So if you're using these languages and you want to use whatever build system, distribution utility, packaging system, or whatever that's built into the language, every language has them, you can use the organizer and set that up to drive that very easy. Or you can use whatever IDE you want, of course, just like Cocoa programming there's no real magic associated with Xcode and Xcode projects. Everything can be duplicated. You can drive TextMate, whatever you want to use.
Xcode's actually leveraging the metadata. So these metadata files exist and Xcode's using it to do code completion in Python and Ruby as well as in the organizer, I believe it's doing some code completion based on the metadata for Objective-C. And InterfaceBuilder actually parses Ruby and Python. So when you go and you declare...
( Applause )
It's very cool. When you declare new outlets and new actions in Python or Ruby and you go over to InterfaceBuilder it's automatically going to reparse those files just like Objective-C and will update the inspector panel and the objects in Interface Builder.
The script that does the parsing is actually a little Python script that does some really ugly regular expression parsing. But if you have a language that you want to support and you do some regular expressions or modify that then file a bug, add it to it, and we'll consider it. So let's look at Xcode-based development.
Okay. Well, that was exciting. No, we don't want Dave Chappelle, that's for sure. There's no kids in the audience, right? So let's launch Xcode. This is the first time I've launched it so it's bringing in the 64-bit stuff. d Then we'll go ahead and we'll do a new project. And let's do a Cocoa-Python application.
We'll do that and we'll call it BobTheApp So you'll see we've got a BobTheApp here. Ooh, I don't have the right preferences, I apologize. So we've got BobTheApp, it's a standard Cocoa app. There's some interesting stuff here. For example, if we go ahead and let's run BobTheApp, it will run it just like a normal Cocoa application, you'll get a window.
There. There's the window. It's up, it's running. You'll see it actually did print out, the application did finish launching. It's because the template does provide an app delegate implementation--and oops. Let's go into our Preferences and make this a lot bigger. Where is it? Fonts and colors. Is there a really big one? Oh no, there isn't. Okay, fine. Let's just do this.
( Typing )
There, now it's big. So got an AppDelegate, it implements applicationDidFinishLaunching, it's connected in InterfaceBuilder. If we were to go back and we were to say-- Do I have the zooming on? Go in here and inspect BobTheAppDelegate. Why don't I have zoom on? Thought I had Universal Access on too but I don't. Let me turn on zooming. Off, on. Okay, excellent.
So let me turn that off so I don't make myself or you sick while I do this. So if we come into here we can say, from objc import IBOutlet, IBAction, so we're going to define an outlet, we can say window = IBOutlet(), spelled right, and if we were going to go back to InterfaceBuilder and inspect this guy, you would see that sure enough the outlet showed up.
So nice integration between InterfaceBuilder, Xcode, and the scripting languages. One other thing I want to show you. We happen to be in a project here, this would work in the organizer too if we go application, okay, fine, it's not going to work for me because this is a demo. Oh, there it is. So you can actually see that we're doing method completions in Python as well.
This works in Ruby. It's actually doing the conversion from the colon style Objective-C notification or notation over to the Python or Ruby style use the under bars. It will fill in the method parameters for you, notating the types, etc. And it's just a matter of a couple of key presses to get to the arguments and fill them in. Pretty cool stuff.
( Applause ) >> Can I have slides, please? Thank you. And there's one other thing that, it's pretty easy to integrate. There's this thing called Pi two app which does dependency checking in Python so if you use a bunch of third-party frameworks, or third-party tools, it will actually go and it will look at all your Python code, figure out all your dependencies and make sure all that stuff gets copied into your final dot app wrapper. That's easy to integrate as well. So this stuff is designed to build dot apps that are drag-and-drop installable too.
So some of the gnarly details here. One of the questions that I often get is can you subclass a Ruby or Python class from Objective-C? The answer is yes, you can. But it's largely an academic exercise. It's really hard to do and the reason why is because in Objective-C you need to compile the class. In Python or Ruby the class comes into being at the very last moment right before execution.
So what you have to do is create a header file that describes the class good enough for the compiler to allow subclassing, that mimics the Python or Ruby side. Now this actually turns out in practice not to be that big of a deal. Because when we talk about these languages what we're largely doing in Cocoa is gluing together a bunch of foundation kind of frameworks. And our glue code, this high-level code, it tends to be exactly that, very high level so Python and Ruby is very comfortable in that role. You don't tend to have the reverse.
So it's just not that big of a deal. PowerPC 32-bit support is a little bit broken in the seed. I apologize for that, it was a last-minute mistake. But I'm interested in it because if you go to the RubyCocoa site Laurent has indicated that you know binary is to fix that or it's not hard to come up with. Native Python or Ruby APIs can be accessed from Objective-C.
But it's kind of unnatural. Objective-C, one of the biggest advantages of Objective-C besides its simplicity, is its incredible expressiveness in its declarations and its syntax. And that means that the APIs tend to be verbose descriptive, almost wordy in some cases. Go look at NSImage or NSBitmapImageRep in particular.
But Python and Ruby tend to have very terse APIs and the end result of which is that if you take a set of Python and Ruby classes and try to put those class methods, the methods of the classes into Objective-C, there's just not enough information there. So it's actually much more natural to create a shim class. With subclasses and Objective-C class like NSObject, so you get all of the Foundation-level behaviors for free. And then either mixes in or wraps the foreign native, meaning the Python or Ruby APIs.
For example, when using Twisted, you might create a subclass of NSObjects that mixes in the twisted.Web class. So now you have a NSObject that can do all this web magic. Garbage collection support? No. It's just not there. It actually, it's because both Ruby and Python have a very strong notion of memory management already built into them and they're not really designed to be subverted with another collector so it's a hard problem, we're looking into it. There are other languages out there that have much more replaceable collectors. I believe Lua is like that. So we're continuing research on that front.
So in summary what you've seen is that we have Python- and Ruby-based Cocoa development with access to just about all the APIs in the system. One of the bubbles that didn't pop up on that slide earlier is that someone asked about the accelerate framework in the lab earlier in the week and it turned out that it was pretty easy to generate metadata for that. So, yes, it's a lot of the APIs on the system.
These descriptions of these APIs are machine readable. It's really easy to generate your own, that's the underpinnings, the foundation that supports all of this high- fidelity bridging that we're doing and allows us to build these bridges such that they can be supported going forward. And we have really tried to design this metadata descriptions such that it can be used for much, much more beyond just development work. Beyond just coding from these languages.
There's third-party support for additional languages in the works already. You can do this work on Tiger with the RubyCocoa work on BridgeSupport as an example and the BridgeSupport project which is already in the open source community. F-Script Anywhere, which is a wonderful tool, small talk derived language, embeddable, interpreted, etc. It's looking to use the bridge support Perl with CamelBones. Shirm's indicated that he has alpha quality implementation now and expects better quality to come along very rapidly.
Lua, I know of at least two maybe three people that are investigating Lua integration and have already started to do some coding work on that. And I'm sure there's other people that are investigating this as well. There's a lab this afternoon, the PyObjC and RubyCocoa Lab, 2 pm. RubyCocoa and PyObjC leading developers will be there as well as a bunch of other people to help you out if you have any questions.