Some developers, mostly those tied to unmanaged languages such as C++ or Delphi, define nativeness as compiling directly to the byte code expected by the physical CPU. That’s certainly one way to look at it, but we think it’s imprecise, and doesn’t take into account the full picture and the complexity of today’s software systems. Consider this for example: if you compile an application for x86, and then run it on a x64 CPU – is that really “native”? Arguably not.
CPU-native, unmanaged code, is becoming less and less relevant these days, and we believe true nativeness of code, of applications, and — most of all — of the developer experience defines itself by other criteria.
Here at RemObjects, we pride ourselves on providing truly native software development solutions for a variety of platforms. We consider two different and separate levels to determine what we consider “native” development: a native user experience, and a native developer experience.
A Native User Experience
What makes a native user experience? For a long time, there was only one platform for mainstream development: Windows. This has changed, and users are, by and large, choosing their work environment by preference. Even when avoiding to get into the “OS wars”, it cannot be disregarded that each platform out there today has its own distinct user paradigms, and that customers usually choose their computing platform carefully and want their applications to work and behave well on that particular system, adhering to their platform’s paradigms.
Windows users expect applications to behave “the Windows way”, and Mac users want them to work “the Mac way”. Similarly, iOS users want applications for their iPhones and iPads that fit in with that platform’s design philosophy, and Windows Phone (7) applications once again need to adhere to completely different UI paradigms than what Android users expect from their platform of choice.
Obviously, the degree to which applications need to fit in with their surrounding platform varies — most games, for example, usually sport more esoteric and platform-independent UIs (after all, they aim to take you out of your daily routing and immerse you in an entertainment experience), while business and productivity applications usually should adhere to the platform most strictly.
Nativeness also isn’t purely driven by using stock UI controls and themes exactly as prescribed. For example, many of the most famous and most well-respected iPhone applications add subtle textures or nuances to the OS provided controls. But they continue to feel “native”, because they stick to the basic UI paradigms, and the controls — while looking visually refined — look, feel and, most importantly, behave according to the user’s expectations.
This is generally achieved by using what is often referred to as the “native widget sets” of the platforms. Whether it’s a standard Win32 “Button” control or a native Mac NSButton, each widget set provides its own unique look and feel — from the spacing of text within the confines of the surrounding rectangle, over font sizes, styles and gradient strengths and directions, to the intricate details of how the control reacts and animates when it is clicked or touched. These subtle details are ingrained in the user’s mind; the user has subconscious expectations for how the control will feel when used, often down to the timing of animations. What may seem like unnecessary attention to detail becomes crucial to the user’s experience, and the user will feel an “Uncanny Valley” effect, if their expectations are not met.
But nativeness extends beyond just user controls. Most operating systems provide a lot of general infrastructure that users are used to seeing and working with — from the standardized Open/Close or Print dialogs on desktops to OS-provided Email or Tweet sheets on phones.
We believe that in order to create a truly native user experience with your application, your development tools need to let you work with the native widgets and infrastructure of the platform. No matter how much effort is put into “one size fits all” libraries, no cross-platform button will ever truly feel like a standard Windows button, like a standard Mac button, like a standard iOS button. In order to treat your customers to a truly native application, you need to choose a development tool chain that lets you work against the native platform.
Currently, such tool chains include .NET/WPF, Metro/WinRT, Delphi/VCL (for Windows), Objective-C/Xcode (for Mac and iOS), MonoMac and MonoTouch (for Mac and iOS, respectively), Java (for Android) or MonoDroid (also for Android).
It goes without saying that here at RemObjects we try to support developers with all of these tool chain choices. On the compiler side with Oxygene, we don’t have full coverage, but we can provide support for .NET and WinRT, all the Mono variations, as well as native Java/Android with our new Oxygene for Java. With our Data Abstract product suite, we have you covered on literally each and every one of the combinations listed above.
But: we believe that a native user experience, while key, is only half the story for successful app development.
A Native Developer Experience
Back in the day when Win32 was the gold standard of operating system APIs (and before), developer tools needed to provide strong abstraction layers for developers to work against — because the OS APIs themselves were so basic that working against them directly was akin to working in assembly language (or was, in fact, working in assembly).
When tools like the later Turbo Pascals, Visual Basic, or Delphi first came out, development against the core DOS or Win16/Win32 APIs was a chore, so developers welcomed abstraction layers that made coding easier, from basic class libraries to UI abstraction layers such as TurboVision, OWL, the VCL, or MFC.
But platforms have evolved, and with the likes of Microsoft’s .NET and WinRT, Apple’s Cocoa (and Cocoa Touch) or Android’s Java-based Dalvik, OS APIs have long reached a level that is developer friendly, a level that can and should be coded directly against.
Further abstractions on top of these APIs are a distraction, not a help to the developer, as they add an extra layer of unnecessary overhead to both the application and the development experience. And such abstractions have repeatedly shown to be failures, like when VCL.NET tried to reconstruct Delphi’s well-liked class library on top of the (already much more evolved and sophisticated, but orthogonal and thus incompatible) .NET Framework (then at early version 1.1).
Unnecessary abstractions of already good (or great) APIs serve no purpose to the developers using them. On the contrary, they separate developers from the platform, and from each other. For every article, sample or blog post provided by, say, Apple or the Cocoa community that shows how to use a feature of iOS, there has to be the equivalent “and here is how you do it in MonoTouch”, “and here is how you do it in FireMonkey”, “and here is how you do it in …”. The abstraction keeps developers from being able to (re)use the platform vendor’s documentation or samples. Worse, it fragments the developer community on the same platform, keeping programmers from interacting and sharing code — every Cocoa control written in MonoTouch is a Cocoa control that can’t be easily reused by a developer using the native Xcode/Objective-C toolchain (nor by a developer using FireMonkey, or Titanium, or any other abstraction layer).
Abstractions also keep developers from adopting new platform features quickly and easily. When the latest SDKs from the platform vendor come out, developers using the native tool chains usually can start working with the new technologies from day one (or even earlier). The very moment a new Android SDK hits, native Java developers can consume the new APIs. The very day a new iOS version becomes available, Cocoa developers can start getting their fingers dirty with the new goodness. All the while developers stuck behind a non-native abstraction layer need to wait for their tool vendor to abstract the new APIs for them.
Not to mention that non-native development tools will often lock you in and force you to stick with the abstraction layer.
The main selling point and attractiveness of a non-native tool chain usually is “familiarity”, the notion that the abstraction layer will let you get into a new platform without the steep learning curve of, well, learning a new platform.
But that is misleading and frequently turns out not to be the case. The main learning curve on a new platform is usually not the development language, or even the class framework — it is getting to know the platform itself and how to write great applications for that platform (looping us back to our first criterium: the native user experience).
Working with an abstraction layer turns out to actually have the opposite effect than intended, by making it harder to understand the platform vendor’s documentation (which is all tailored towards the platform-native tools, of course) and making it harder to re-use existing code samples found online.
To go back to the example of iOS: having to look at and understand documentation or samples written for Cocoa’s Objective-C APIs might seem difficult, if one is not yet familiar with the paradigms of Objective-C. But what’s actually more difficult is having to look at that same documentation or sample and then having to figure out how it translates to your — say — C#-based abstraction layer.
And no abstraction layer is ever perfect. Eventually, the underlying platform and its APIs will leak through and will have to be dealt with. Any efforts to avoid getting to know and understand the platform before developing on it is misguided and only delays the inevitable. Eventually the abstraction is missing a feature and the developer has to dig down to the base APIs and implement it themselves. Or a bug arrises that needs more debugging than the abstraction layer supports.
As with the native user experience, here at RemObjects Software we have made it our mission to support (and encourage) developers to use platform-native development tools; you’ll notice we’ll even encourage developers to go for the more native tool chain in cases where recommending a less native solution would mean an additional sale for us.
On the compiler front, Oxygene supports platform native development on classic Windows, Metro (Windows 8) and Android, and it is also a great choice for cross-platform servers based on Mono. The one notable omission is iOS and Mac, for which we recommend the excellent Xcode and Objective-C, even though Oxygene can support it via non-native development tool chains such as Mono and MonoTouch or Java.
With the Data Abstract suite, we support native development for all current major platforms: classic Windows (via .NET and Delphi), Windows Phone, Mac and iOS, as well as Android and other Java-based platforms (Data Abstract for Java is currently in beta).
Other Concerns, Such as Code Reuse
We realize of course that other factors play into the decision for the development tool chain. Considerations such as code reuse might rank higher on the list of priorities than a native development experience, and that is ok.
Our philosophy is that, all other things being equal, developers should aim for a toolchain that is native on both accounts: user experience and developer experience. If necessary, a compromise in developer experience can be made — it is after all a compromise that affects only the developer, if pursued correctly. No compromise, in our experience, should ever be made with regard to providing a truly native user experience.
In terms of code reuse between platforms, we at RemObjects Software aim to help, both on the language front (by providing the common Oxygene language as developer-native language for .NET, WinRT and Java/Android, at least) and on the back-end database front, by keeping Data Abstract wire compatible and letting you share a common server infrastructure for all your native client applications.
You can find out more about our cross-platform, platform-native products, Oxygene and Data Abstract, at remobjects.com/oxygene and remobjects.com/da, respectively.
And you can find an overview matrix of how our products fit into the various native (as well as semi-native and non-native, if you must go there) development chains, at remobjects.com/products/toolchains.
Thank you for reading, and we are looking forward to your feedback and comments.