NativeScript Cheat Sheet & Tips

September 1, 2017

I worked on a multi-year project for a platform that delivered online education to thousands of students each year. For those students using mobile iOS/Android platforms, we provided a NativeScript application that used a blended web-and-native experience to deliver content to students.

NativeScript (like any write-once-run-anywhere mobile solution) definitely gets trickier as the abstraction leaks to reveal Android/iOS-specific issues, but if you build a base-layer of platform specific knowledge, you can get quite far.

I learned a bit along the way, and want to share with you!

Troubleshooting: It's broken and I have no idea what is wrong

NativeScript app builds can be finnicky. There's a lot of moving pieces necessary to construct your app. Sometimes, things get out-of-whack. It's a scientific thing.

Here's my favorite bash-script recipe to obtaining a "clean slate" on which to verify that your random, weird little problem is truly a persistent one worth wringing hands about:

#!/bin/bash set -e rm -rf hooks/ rm -rf node_modules/ rm -rf platforms/ tns platform remove android || true tns platform remove ios || true npm install tns platform add ios tns platform add android # Carry out builds just to see if they work! tns build ios tns build android

Troubleshooting: Type 'X' is not assignable to type 'X'

You've somehow managed to pass an object of type X that is actually of a different "version" than the thing you're assigning to. A TypeScript issue that typically pops up in conflict with nativescript-* packages.

To debug, figure out what package type X belongs to, and run npm list to locate the package and see where it is duplicated – you'll likely find the package listed twice under different version numbers.

You can then use package.json to pin to a specific version you prefer. If the conflict is with a nativescript-angular related package, it's advised that you let the nativescript-* package version preference win.

Profiling memory on Android

You'll want to read a primer, first: Android Performance - The Why and The How

You'll need to use the Android device emulator, NOT Genymotion.

Here are the general steps to getting started with profiling

  • Run tns build android
  • (Install) and run Android Studio, and use it to start the emulator (don't worry about opening your project)
  • Run adb install platforms/android/build/outputs/apk/your-file-here-debug.apk (replace your-file-here-!) in your NativeScript project directory
  • Now, in Android Studio, look for the dropdown to select your running app
  • Finally, select a monitor (e.g. Memory Monitor)

Try running tns prepare ios --release, and then open the platforms/ios/*.xcworkspace (replace the * accordingly!) file in XCode, then launch using the Play button. This usually works, and magically fixes the issue for subsequent calls to tns run ios.

Profiling iOS memory usage and debugging memory leaks

While you cannot set a "maximum memory" threshold in the simulator, you can profile memory usage using Instruments. Instruments is a bit funky and may sometimes require you to force-kill it; sometimes, it refuses to show profiling results, or menu options disappear!

To start, you can trigger a "low memory warning" using Instrument > Simulate Memory Warning. This triggers garbage collection, which should flatten out your memory usage to mostly-just-what's-really-being-used. It's a good way to verify memory is actually leaking.

To start profiling, launch the Instruments app while the iOS simulator is running. Near "Choose a profiling template for:", select the emulator and then the running app.

You can use Allocations profiling to monitor object allocations. You must select some sort of statistic to gather, click record, carry out some app activity you want to profile, and then stop recording to see results.

To find leaks, watch "Persistent bytes" – that is, what was allocated, but not de-allocated. Repeat the same activity that you think is causing the memory leak, and then see if Persistent bytes becomes a line (i.e. stops growing) or continues unbounded growth over time.

For more information:

Debugging iOS segfaults

For example, Service exited due to Segmentation fault: 11 when running Simulator.

You will need to go to ~/Library/Logs/DiagnosticReports/ and find the most recent crash report. This will include a full stack trace with debug symbols that may help you trace the problem back to what is causing the segfault.

Segfault code 11 is a memory access problem – for example, accessing deallocated memory. You've potentially uncovered a NativeScript bug in its native interface code – e.g. using components in a way they were not intended to be used, and/or using certain methods at the wrong point in their lifecycle.

Troubleshooting: "tns publish ios" fails with "Your app can’t contain standalone executables or libraries"

Same as this problem. You can use filter-modules.json to remove the problematic module – though be sure your app still works afterward!

Genymotion makes development easy

Starting with the vanilla Android emulator gets you started, but can become frustrating from two perspectives:

  • It's a bit slow
  • It's tricky (or near impossible) to use a local dnsmasq configuration so that your virtual Android instance can connect back to a custom Vagrant-exposed API that lives on its own virtual interface and IP – you can perhaps get this working with the Android emulator -dns-server flag, but as of writing, it's not clear how to do this in conjunction with NativeScript.

Genymotion is pretty fast, and seems to be using NAT via your host machine – for me, it simply used my local dnsmasq server configuration, so I could easily access my favorite local-only *.test hostnames that pointed back to running vagrant instances of my API.

By Daniel Starling

Software consultant in Portland, OR

Contact me

Daniel Starling

Software consultant in Portland, OR

Need help? Contact me!