Setup - All Engines
Assuming you have all your dependencies installed according to there respective pages:
- React Native: https://reactnative.dev/docs/0.60/enviroment-setup
- NativeScript: https://docs.nativescript.org/environment-setup.html
- Flutter: https://docs.flutter.dev/get-started/install
React Native Doctor
Each of the three tooling's has a Doctor command, this command checks your setup and verifies everything looks sane for doing development in that framework...
The React Native doctor seems to be mislabeled, it calls the SDK BUILD tools, "Android SDK". It apparently want the Android 31.0.0 BUILD tools, not the actual Android SDK 31. I had all those Build Tools, but also every SDK since 26 installed and was quite confused at what it wanted. Finally actually checked the Build tools tab in Android studio and realized what it was looking for.
The interesting thing, unlike NativeScript tooling, even though the RN Doctor complained about the missing tooling, the CLI continued to work and it built the application without any other issues. My guess is that they have "tested" it with 31, but it works fine with 30 or 33.
React Native Project
For React Native (RN) to create a project the recommended method is:
npx react-native init [your project name]
And you will see something like this:
So it basically gets you up and running quickly, and gives you the (in my case broken) instructions of how to start your app.
In addition if you have never ran a RN app before it says it has to Jettify 945 files. I assume it caches the results, because the first time was very slow, each following run this step appeared to be instantaneously.
Following the instructions of changing the directory and doing a "
npx react-native run-android" gave me an BROKEN app in my emulator. Not the best on-boarding. Having the out of the box app be totally broken on first run is disheartening and can easily turn people off to the platform. At least It had a nice diagnostic screen that told me I needed to run "
npx react-native start" and then hit "r-r" on the keyboard to force the app to refresh, which then it gave me a working app. In addition using --variant release also gave me a working application.
NativeScript requires you to manually install quite a few dependencies, by contrast I found React Native's & Flutters install a lot easier for novice users.
To run the NativeScript doctor you type
> ns doctor
Now in my case because the NativeScript team is unfortunately slow even fixing known issues (even those with a supplied fix) -- Which I personally posted several months ago (in May) -- the most recent CLI (published 2 days ago) is still failing on my machine with this error, so unless I manually apply my fix that I submitted to them (https://github.com/NativeScript/nativescript-cli/issues/5668) -- NativeScript is totally broken on Linux. You can see why I wrote the "Goodbye NativeScript" article, its ridiculous now how many outstanding issues don't get any traction or fixes, let alone ignoring fixes that are supplied.
The crazy thing is NativeScript's doctor is quite wrong, and, then the NS tooling will then refuse to actually work for ANYTHING related to the platform when it detects what it considers is an "error". NativeScript itself doesn't actually use Javac, but it checks because Gradle uses it. But it detects the wrong version first and breaks.
I have several versions installed which Gradle is actually smart enough to use the proper version it needs to (as the Gradle version NS is using doesn't currently like v18 of Java). It is just that the NS CLI doesn't understand how to parse the version 18 number and then it doesn't bother to search for any other versions. If you are on Linux you will run into this issue if you have Java 18 installed. Other platforms you probably won't run into this specific issue, but the worst thing is that when you run into any detection issues, it will completely stops you from doing anything with NativeScript on the platform it thinks has an issue.
To create a project in NativeScript you type
> ns create [project name]
It will then ask you the flavor to use, which is one of the awesome and unique things in NativeScript. You can develop your application using a framework you are already familiar with. The best maintained is plain JS/TS (which is the core flavor, so it has to be maintained), then Angular (latest version), finally Vue (only Vue 2.x). The rest of them are in varying states of work-ability of which you are liable to have some issues with. This isn't to say Angular or Vue is perfect, but those two flavors normally have the least number of issues compared to all the other flavors. Finally If you are wanting the fastest or closest to the metal flavor, JS/TS is by far the fastest and least memory using flavor. All other flavors are using their (in some cases massive) frameworks wrapped OVER the plain core framework. So a call to do anything with a component is several more levels deep in code in say Angular than plain JS. Last time I benchmarked things, Angular was a order of a magnitude slower than plain JS for most things, not to say it was horribly slow -- but 15ms vs 10ms adds up quickly in complex screens.
So creating a project prints this out in the current version of the NativeScript tooling.
The instructions here do sorta work, for example it prints "ns run ios", but I'm on Linux -- guess which two commands only work on Macintosh and don't work on Linux or Windows? Kinda silly to tell me to run a command that will fail as the first command. But the other two commands for Android do work.
The Flutter doctor is ran by typing:
> flutter doctor
> flutter doctor -v
Now of all the Doctors, this one is the most impressive. It has a lot of checks that are useful. They clearly have thought of a lot of corner cases and I was a shocked when it told me my API 33 wasn't installed properly. This was a side effect of me messing and trying to fix the React Native Doctor. I started installing the latest API's and deleting old Preview API just to make sure they weren't causing RN's detection issues. So the directory existed, but Api 33 was still being downloaded by Android Studio, and Flutter detected this and let me know there was an issue. Kudo's to the Flutter team for the very valuable checks to make sure that the API's are installed properly.
Using the "-v" does even more in depth reporting, which actually is longer than an entire screen of information -- so I'm just going to show you want the normal one prints, since we don't need a two page graphic here.
The Flutter doctor is also smart enough to actually find the (older) Java 11 install which Gradle uses, rather than detect my newer default version that I use for other development work...
To create a project, if you haven't guessed it is:
> flutter create [project name]
And you get this:
I was shocked when it finished in 2 seconds, I'm used to NativeScript taking forever and React Native actually was longer than NativeScript. So it was a pleasant surprise to hit return and be right back at the command prompt, ready to go. I also like that its required two commands are together and simple to follow and worked out of the box. So far this is the only platform that actually worked without having to figure out what was broken or work create fixes.
The first comparison I plan on testing is speed to first draw. I used one of my slower android devices to make it easier to see the actual speed of the application. This is actually pretty critical as studies show that the slower the app is the more likely users will use the faster app. So if you are competing in a market, do you want the app that starts instantly, or the one that start up a dead last?
To build what I believe is a release apps (if I made any mistakes here, please let me know), I used the following CLI options
Flutter (flutter run --release )
NativeScript (ns run android --env.aot --env.uglify --release --key* )
React Native (
npx react-native start --variant release)
Speed to first draw
|React Native (Hermes)
|Arm 64 Target only
|21.8 megs (*)
|React Native (Normal)
|React Native (Hermes)
(*) Flutter does not ship the x86 32-bit library. However, based on the 3 platforms it does ship we can estimate that the number should be somewhere around 21.8 megs total to include the 32-bit x86 runtime if it had one.
|React Native (Hermes)
I expected putting Flutter on old of a phone (low/mid-range early 2018 Android 9 model) would cause it to be handicapped as its GPU is old, and has low ram availability -- Flutter tends to depend on the GPU. I was very surprised by the outcome so much so I decided to skip all the numbers for a much more modern faster device since it performed incredibly well on this device and easily lapped the other two frameworks. My initial testing on a faster device showed it was equally faster on it.
NativeScript benchmarks, as I stated above adding a framework over the core framework actually cost NativeScript a tad over a second in startup time. That is almost all initialization time from the Angular framework. It also uses twice as much memory. Now granted the Angular demo is slightly more complex than the JS demo as it uses a list view, instead of a button and some text. It is still one of the simplest list view you can imagine and it shouldn't be costing anything close to an extra second of time for first paint and twice as much memory. Also in actual complexity the React Native demo actually displays a LOT more elements on first draw than either of the NativeScript app's, yet it consistently starts up almost 2 seconds faster than either of the NativeScript app's.
In the next article I plan on comparing the same information on iOS and see how well everything does.
So far, Flutter is easily the winner in all three aspects: App size, memory usage and speed on Android.
If anyone has any pointers to a simple TODO app using SQLite on NativeScript, React Native and Flutter, I would love to compare a more "like vs like" app test, not that the examples don't give us a good idea of memory, size and startup time. It just "appears" more fair if everything is "like".