Hacker News new | ask | show | jobs
by ryandrake 1744 days ago
> Inconsistencies both small and large had crept into our apps over time. From small things like password strength being different between platforms to larger things like differences in search results and entire missing features.

The solution that has worked well for me in the past, has been:

1. Keep all of your business logic and as much of the rest of your code as possible in platform-neutral C or C++: Something you can call into from all native platforms.

2. Write a very small layer of code using the native language/frameworks to do the UI and interact with platform-specific APIs.

This has a few advantages: You get a single code base, for the most part, to maintain. You have the opportunity to implement as native a look-and-feel as you want, on each platform if you want. Most of your application is C or C++ so you don't need a team with deep expertise in multiple languages. Most of your development should be on the business logic, right? This architecture also lets you easily add a command-line version of your product, for example to help with automated testing of your business logic. And finally, you don't have to ship a huge, memory-devouring browser and JavaScript stack with your application--your users will thank you.

It's not rocket science or anything to be terrified of! I think people too quickly dismiss cross-platform native because they imagine re-writing their application in 3-4 different languages, but it doesn't have to be that way.

6 comments

> Most of your development should be on the business logic, right?

In "creative" applications, the term "business logic" is sometimes hard to use, but accepting it as "stuff unrelated to the actual user-interaction" ... I can say that in Ardour (a cross-platform DAW), the UI is ALWAYS by far the hardest component of everything we do, and takes far more lines of code than anything in the backend (roughly speaking, where the "business logic" lives).

DAW - digital audio workstation. https://ardour.org/
IME for a lot of apps, most of the development is the UI. Lot's of apps have most of their business logic on a server these days anyway. The problem is not typically how to share business logic, it's how to avoid writing 5 different UI's that do the same thing.
Have the UI defined in JSON or whatever rocks your boat, even HTML, but then map those concepts to native widgets.

Some native apps on Android/iOS do exactly this, with UI coming from Web API calls.

Isn't this exactly what a system like react-native provides? Just off the shelf for commom patterns, and backed by a turing complete language. If you have yo build out the UI toolkit yourself yhen you haven't really saved much.
Not at all because it isn't native to the platform.

These solutions use the native SDKs of the platform directly.

So you are e.g. using Java/Kotlin on Android and Swift/Objective-C on iOS.

React native brings JavaScript and VM for the ride.

Is a JavaScript VM really that different to intepreted JSON files? With react-native, the UI controls are using the platform native toolkits, there's just an abstraction layer on top.

Checkout https://github.com/gorhom/react-native-bottom-sheet for example.

Definitely, because first JSON was just a possible format, secondly, whatever format is used, it is serialisation format for a native language, using the native APIs of the platform.

There is no interpretation going on.

The abstraction layer for react native has JavaScript engine in the middle, plus a marshaling layer to go through OS APIs, and on Android is double slow, because NDK only has game related APIs, anything else requires a second marshaling call to go through JNI.

I had this experience as well!

I think a big limitation to this approach is actually finding (competent) C++ devs who are willing to work on mobile, otherwise it works really well.

Would it be possible to use Go for this? That compiles to native code, although it does include a much more significant runtime than C, it's safer than C or C++, and it's famously easy for developers to pick up as as second language.
Probably not very well, as these solutions typically involve FFI, and integrating thw runtimes would be a big pain. On the other hand, Rust is ideally suited foe this use case. Personally I'd love to see a Rust + react-native combo.
Multiple event loop integration is something projects get to very late in their lifecycle, if they ever get to it at all. Tokio is no exception: https://github.com/tokio-rs/tokio/issues/2443

When I used to write more networking C code, I used to be careful to write my libraries to be event loop agnostic. And if my library had its own event loop, I made sure that it could be stepped independently so it could sit atop another application event loop. This is alot easier on modern Unix systems because kqueue and epoll can be stacked--you can poll a kqueue/epoll descriptor from another kqueue/epoll descriptor, so sockets, timers, etc registered with the first will bubble up to the parent and your library-specific event loop can just export a single file descriptor to be polled by another event loop.[1] Windows doesn't have such a capability so there's no simple way to bubble events.

Of course, you can run your different event loops in different threads, but then you have to deal with the nightmare of shared data multithreading. Rust makes multi-threading "fearless" because it heavily restricts multiple threads from holding references to the same object, so Rust doesn't help in this regard, especially considering you're integrating with non-Rust code and, worst of all, GUI APIs, which tend to be some of the most inscrutable and hostile code with which to integrate.

[1] This is effectively structured concurrency as a first-class kernel interface, an elegant characteristic of kqueue and epoll that is sorely underappreciated and rarely used.

The thing with C and C++, is that they are part of the SDK, anything else is on you to improve the development experience, integrate libraries, or debug FFI issues.
Not easy because they must also be willing to endure the 2nd class tooling that Google creates for C and C++ on the platform, on top of the usual development issues.

But still, it is much enjoyable than using any other framework that adds even more layers to the puzzle.

> Keep all of your business logic and as much of the rest of your code as possible in platform-neutral …Something you can call into from all native platforms.

This to me is the big win of Webassembly: you can compile with gcc/Clang to target it as well. It makes the browser another “native platform”

I actually agree and chose C++ as well, but eluded that part of your quote because it could be a more general choice than just those two.

In theory, in practice the tooling is a pain to deal with versus traditional native workflows, and we are stuck with MVP 1.0.

https://www.youtube.com/watch?v=ElCnUIr5XtI

Indeed, this is the best approach for cross platform native applications.
C and C++ are awful for business logic. You will be outcompeted by teams using JS or TypeScript.
Despite my usual rants, that is a matter of developer competency and development processes, not programming languages.
ok zoomer