What is “Nougat”? — Part 1
This is the first in a series of posts about “Nougat”, in which i want to go into a few more details about our plans and the technologies behind Nougat, so you can get a feel for what to expect from the product, as we wait for the first beta to be made available.
So what, exactly, is Nougat?
Nougat is a third independent target for the Oxygene language and compiler, targeting the Apple Cocoa and Cocoa Touch development platforms.
More strictly speaking, Nougat is Oxygene for the Objective-C Runtime.
What does that mean?
Similar to Java, the term Objective-C has overloaded meanings. Of course there is Objective-C, the language, which is what most people think of when they hear the name. It’s the language that Apple’s Xcode IDE uses to let developers create native applications for both of its platforms: (Mac) OS X and iOS.
But underneath the language (and to a large degree influencing how Objective-C as a language works) lies the Objective-C Runtime (OCR, for short). That runtime (oversimplified, a set of C APIs) defines how objects look in memory, manages run-time type information, handles the passing of messages (a.k.a. method calls) to objects, and so on.
The way i like to think about it, the Objective-C runtime puts Cocoa someplace in the middle of “managed” platforms (such as .NET and Java) and “unmanaged” development architectures such as Delphi or C++. So when talking about this, i often call Objective-C “half managed”.
“Nougat” brings the Oxygene language to this Objective-C runtime, in that it compiles Oxygene language code into objects that natively live in this runtime, and seamlessly interact with other objects in the same runtime. It is not a “bridge” (such as MonoTouch, RubyCocoa, or the (now deprecated) Java/Cocoa Bridge, but a fully native second language — in no way second to the Objective-C language itself — that runs on this runtime.
This is important for several reasons:
You’re creating true Objective-C objects
The classes your define in your Nougat application will compile down to true OCR objects. The runtime will see these objects and interact with these objects in the same way that it does with objects created by an Objective-C developer in Xcode. In fact, the runtime cannot even tell them apart.
When you, say, define
type RootViewController = class(UITableViewController)
then this “RootViewController” class you are defining will be a true descendant of UITableViewController — with no bridging magic inbetween. If the table view sends a
cellForRowAtIndexPath: message to get data to display, the runtime will dispatch that call directly to the
cellForRowAtIndexPath() method that you hopefully implemented.
You’re interacting with true Objective-C objects
Just like your classes are true Objective-C classes, all the classes and object instances you interact with will be true Objective-C classes, as well. If you hold a string object, that’s a true NSString instance, and if you are calling
you are simply sending the object the
hasPrefix: message, just as an Objective-C developer would be with
[myString hasPrefix:@"foo"]. (Note that Objective-C’s message sending is nil-safe, and thus corresponds to Oxygene’s colon operator, while a call such as
myString.hasPrefix('foo') would throw an exception on nil calls, as you would expect from Oxygene code.)
No awkward manual mappings
Just to be completely clear, there is no manually defined “mapping” between APIs defined in Cocoa and the APIs Oxygene sees (or vice versa), as there is in most Cocoa bridge solutions. There’s no magic table in Oxygene that tells the compiler how to match a call to
hasPrefix('foo') to the right method on the NSString.
The Oxygene compiler, instead, simply has full direct knowledge of the Objective-C APIs and does those calls directly — just as Objective-C itself would: it takes the name, packs it up in a selector, and calls the OCR’s objc_mmsg_send() function.
In fact, if you have a variable of type
dynamic (or, aliased to that type,
id), Oxygene will let you make just about any known method call on that object, just like Objective-C does.
Beautiful Cocoa-style method calls
This also means that Oxygene has full support for Objective-C’s more verbose method naming convention — something that is crucial, really, as a developer would want to interact with the Cocoa APIs as they have been designed and intended (for better or for worse — i personally find Cocoa beautiful).
I purposely picked a simple and uncontroversial example with
hasPrefix: above, but what Cocoa APIs are known for are of course the infamous “muti-part” method names, where the method name reads like a sentence with the individual parameters interspersed in between. Consider for example
stringByPaddingToLength:withString:startingAtIndex:. How would you call this in Oxygene?
Most bride-based solutions go for one of two options: they would either provide an (often overloaded) method named after just the first part, e.g.
stringByPaddingToLength(20, " ", 5) or they would concat the entire name into one big chunk of text, such as
stringByPaddingToLengthWithStringStartingAtIndex(20, " ", 5).
For one, yuck. For another, this would require manually crafted mapping tables, which Oxygene does not use — as explained above, Oxygene just handles the Cocoa APIs, any Cocoa APIs, straight up.
So how do you call this method in Oxygene? Simple:
myString.stringByPaddingToLength(20) withString(' ') startingAtIndex(5);
Simple, elegant, and it preserves the “readable as a sentence” aesthetics of the underlying API. What’s more, the syntax seamlessly works with any method an OCR object might declare.
The same goes, of course, for declaring methods in your own classes — both when implementing things such as delegates, but also when designing your own class APIs in Cocoa style:
method stringByPaddingToLength(aLength: Integer) withString(aString: String) startingAtIndex(aStartIndex: Integer);
With this first post, we have only just begun to scratch the surface of “Nougat”. Stay tuned for more in this series over the next few weeks.
And let us know what you think in the comments below!
See also part 2 . . .