23 Feb 2017

What I Look for in a Hybrid App Framework

Since iOS and Android became popular, we've seem dozens of frameworks that let you target both OSs with shared code, to a lesser or greater degree. These frameworks are all over the map in their design choices. But which of these design choices are critical to build a top-quality [1] app? Which are the ones you can't afford to get wrong?

Here's what I would look for when picking a hybrid app framework:
  1. Uses native views, not HTML.
  2. Lets me reuse UI code that's common between iOS and Android.
  3. Lets me invoke any and all platform-specific APIs, like 3D Touch, rather than forcing a lowest-common-denominator app.
  4. Is as responsive as native apps.
  5. Performs as well as native code, in CPU and memory.
  6. Lets me code in Swift or Java, as opposed to a third language.
Let's discuss each factor.

(In this post, when I say "native app", I mean an app built using the OS's default framework and language, like Cocoa Touch and Swift on iOS. Anything else, anything involving shared code or a third-party language or HTML, is "hybrid".)


Native Views

UI should be built using native views, not HTML, which in my experience is second-tier, despite framework developers' marketing. Maybe HTML will eventually get to parity. Or maybe it already has for specific apps. But as of today, I'd hesitate to use HTML, to avoid the risk of having to rewrite my app, as happened to my team [2].


Reuse Common UI Code

Just because I'm using native views doesn't mean that I want to write common code twice, say to create a button, set a label, set a background color, and add a tap listener. Both Android and iOS buttons support these functions, so I should be able to write this code once that works on both platforms. That's less code to write, debug, polish the UX, and maintain. The hybrid app framework should provide wrapper classes that are implemented on top of the native views, and present a common interface across Android and iOS. That is, when I do:

button.text = "OK"

it would translate to:

UIButton.setTitle("OK", for: .normal)

on iOS, and:

Button.setText("OK")

on Android.


Platform-Specific APIs Should Be Available

At the same time, if I want to do something that's available on only one platform, like 3D Touch on iOS, I should be able to. The aforementioned wrapper class should give me a reference to the native Button class, so that I can enable 3D Touch on it, in this example. Absent this, the hybrid app framework drags apps down to the lowest-common denominator. I wouldn't want to build, or use, such apps. I want apps as good as native apps. Sharing common code should only be an implementation detail that doesn't affect the UX.


Responsiveness

I tried a NativeScript app, that too a showcased one, and every tap had a lot of latency. I'd tap and wait a noticeable amount of time for it to respond. I haven't used such a laggy app in years. It reminded me of a 5-year-old Android phone. I don't want such crappy apps or frameworks. Hybrid apps should as responsive as a native app.


Performance

Hybrid apps should ideally perform as well as native apps, since that enables whole classes of apps to be built that would otherwise have to be native. For example, I'm planning an app that processes gigabytes of data on the phone. I can't use a hybrid framework that requires more CPU or memory.

Faster baseline performance is also good because there will be less need to optimise. I'd rather use Swift and not optimise my code, than use Javascript and end up having to optimise it. The real problem is that if I pick Javascript, I don't know ahead of time whether I'll need to optimise later. If I knew that building an app in Swift would take three months, and building it hybrid would take three months plus another month to optimise, for a total of four, I can make an informed choice one way or the other. But when I don't know ahead of time how long it's going to take, that adds uncertainty to the project. I may even find myself having to rewrite it, as happened to my team [2]. Using a hybrid app framework is a risk. And as with any risk, some people decide not to take it, and to instead build a native app. If hybrid apps were as fast as native, that risk would reduce.

Hybrid apps that use Javascript are at a disadvantage when it comes to performance. Other dynamically typed languages are not even an option, since they're even slower, and Apple doesn't allow interpreters. That leaves only statically typed languages.

Which means one of the native languages on Android and iOS — either Swift or Java — as opposed to some other language like C#. The last thing I want to learn is a third language. Platform APIs are in the native language, so they're easier to invoke from the same language [3]. Documentation and sample code, whether on Apple's/Google's site or Stack Overflow, are in the native language. So are third-party libraries. And mobile developers are already likely to know Swift, Objective-C and/or Java, not C# or some other language. Even if they don't, there's a benefit to learning the native language, because it may be useful in a future project. IDEs and other tools support the native language much better. And so on.

For all these reasons, I'd want to code in either Swift or Java. Then my code will derive the above advantages on one platform. If I code in Java, it will fit in naturally on Android, while requiring a mapping on iOS. If I code in Swift, it will fit in naturally on iOS, while requiring mapping on Android. But if I code in a third language, I'll have to figure out how that third language maps to iOS AND to Android. That's two different mappings I'll have to understand. I pay all the above costs twice instead of once. Whereas, if I use either Swift or Java, I have only one mapping to worry about. I pay the cost only once.

Even Java is too slow as of today. I can write my business logic in Java, and cross-compile it for iOS with J2Objc. But J2Objc is 40% slower than Swift according to one benchmark. I would think twice before using it for my performance-sensitive app, as things stand today [4].

A more promising option is to write common code in Swift and invoke it using JNI. It's not ready, but if and when it is, I'll hopefully be able to avoid this 40% overhead [5]. I'll benefit from Swift being compiled, rather than using a VM, as Java does. Swift is also more efficient than Java. For example, Swift supports structs, which can be allocated inline. An array of a million structs requires just one dynamic memory allocation in Swift, as opposed to a million in Java. It's not just allocation, but also eventual deallocation, and requiring an indirection for avery access. Similarly, a struct that's a member of another struct or class, or a local variable, doesn't require memory allocation. In Java, to have a List or Map or Set of ints, I need to box each, which adds a lot of overhead. Swift's unsigned int lets me efficiently and conveniently handle an image buffer, say. A 12-megapixel uncompressed 8-bit image takes 36MB, but if I use 16-bit ints in Java because it doesn't have 8-bit unsigned ints, it balloons to 72MB. Conversely, I don't find anything in the core design of Swift that would make it slower than Java.

In summary, here's what I would look for when picking a hybrid app framework:
  1. Uses native views, not HTML.
  2. Lets me reuse UI code that's common between iOS and Android.
  3. Lets me invoke any and all platform-specific APIs, like 3D Touch, rather than forcing a lowest-common-denominator app.
  4. Is as responsive as native apps.
  5. Performs as well as native code, in CPU and memory.
  6. Lets me code in Swift or Java, as opposed to a third language.


[1] You don't always need a great UX. Like in a line-of-business app, which a company can force its workers to use even if each tap incurs a noticeable lag. Or a two-person startup or a charity that doesn't have the resources to build the UI twice, and will take something over nothing. Or if you already have a hybrid app and you can think of higher priorities than rewriting it.

In this post, I exclude such apps from the discussion. I focus on top-quality apps.

[2] In 2010. That particular app may work fine today as a hybrid app, but maybe others don't. It's still a risk that you find out too late that your particular app doesn't work well. So I'll wait till there are more native apps are built in HTML that I use regularly and find to work great. And till there's an emerging consensus that HTML works great for native apps.

[3] iOS APIs are all implemented in Objective-C, not Swift, but Apple put in a lot of effort to make the two languages have similar abstractions and interoperate naturally.  The brdiging is done so well that bridged Objective-C code looks like idiomatic Swift code. Which is not the case with C# or Javascript.

[4] This could be optimised in the future, at which point Java might again be a contender, but it isn't as of today, for performance-sensitive apps.

[5] Or whatever the number turns out to be for your app.

No comments:

Post a Comment