Category Archives: Tips

PSA: NativeScript 6.20 - Is a Breaking Change release!!!

Upgrading to 6.2.1 and the latest Nativescript-angular (if using angular) and the latest proplugins; should fix MOST issues that 6.2.0 had. However, I still highly recommend the webpack rewrite rules at the bottom of the post to solve ALL the issues.


NativeScript follows the SemVer process; so normally they would have bumped a major version on breaking changed. This break is mostly an un-attended side effects of a couple things. And unfortunately they didn't realize it broke things, so we now have 6.2 which breaks things... Oh well, mistakes happen, life will go on. And so this post is to make you aware of what things you want to change when you upgrade to NS 6.2.0...

To Upgrade or not to Upgrade, that is the question!

I do want to make sure I am VERY clear -- I do NOT think this was a bad change, nor do I think NS 6.2 is a bad release! My apps will be upgraded to it. It offers a lot of good features and fixes. It is just that being aware of the issues will help you make sure you don't have any issues with it when you do upgrade to it.

Lets dig in to the issue.

So lets get some details, first about 3 major versions ago in 2017, my business partner and buddy Nathan Walker proposed on issue 4041 to move the nativescript tns-core-modules into its own namespace. @nativescript along with other nativescript pieces being put into that parent namespace...

So based on this you might guess where the source of the issue lies now.

If you read the above issue; you will notice Peter (another awesome developer) and I both recommended that they do this in the 6.0 release. I personally felt like a hard break would be the safest course of action. Just eliminate tns-core-modules totally. Since they already were totally breaking the build tools, and also breaking other plugins things in v6.0 -- they might as well add this to the pile so we deal with all the breaking changes at one time in plugins. Unfortunately, our advice wasn't taken, or it was missed and so here we are with a very interesting breaking change that I honestly did NOT foresee when I recommended they do it in the initial 6.0.

There are three different issues here that stem from the same cause:

First, if you are using NativeScript-Angular and use any third party plugins -- you might find that they are failing because NativeScript-Angular was moved into the @nativescript/angular namespace. The compatibility imports for the old namespace; is apparently missing a couple redirects so plugins trying to load them fail. I'm sure these plugins will be updated shortly; but right now they are broken. (This has been fixed in the latest version of NativeScript-Angular)

I do NOT know yet if NS-Vue, NS-Svelte, or React-NativeScript have any issues; but odds are a fairly low with them because they are third party and were not moved into the @nativescript namespace, so apps depending on this probably are NOT affected directly (other than by any other plugins.)

Second, because of the design of the tns-core-modules redirect, the redirects waste a little bit of running memory. If your app or plugin imports tns-core-modules/BLAH (which all apps do before 6.2) then webpack will give you Object #1 tns-core-modules-blah, however, when a plugin or the core modules loads @nativescript/core/BLAH you get Object #2 nativescript-core-blah -- they LOOK the same but they are different objects each using memory. This does increase a little bit some parsing and memory that the V8 has to use because you now have basically more than a single framework in memory. Some of this is mitigated based on how objects are tracked; but some of it isn't. So this creates some other un-intended side effects of little bit more GC pressure, more ram usage, and the nasty pitfall we will discuss later...

Finally, if you are using any plugin that does any sort of class monkey patching, they will now all fail -- in this category is things like my very own nativescript-platform-css, nativescript-orientation. The reason why is because of the above allocation issues. Object #1 != Object #2. monkey patch Object #2, doesn't monkey patch Object #1. This is a corner case; as it requires you to actually replace the class (i.e. the top level object). So any plugins that rely on nativescript-global-events which extends and enhances the page class; were affected.

As an aside: The good news is virtually every one of my plugins on https://proplugins.org has been updated to have a NS 6.2 version. If you find a plugin that no longer works after you updated to 6.2; let us know and we will attempt to get it fixed. The 6.1 -> 6.2 fixes are very minor; so they can be rapidly put into the plugins.

One final note -- Please see the method to update the webpack at the bottom to have webpack rewrite tns-core-modules to @nativescript/core this is still highly recommended as some plugins will still be broken if you are still using the tns-core-modules redirect version.


The low level nitty gritty details - Proceed with caution.

For those who are interested in the nitty-gritty; we will walk through why #2 and #3 occur in NS 6.2. Please note this is VERY simplified -- The actual TS -> JS code is a bit more complex and a wrapped object. However, this should give you an idea of the why's...

V8 basically keeps a object map of how a object looks in memory; so if I have file "Blah" with class HiYa -- that looks like this:

export class HiYa {

   function hi() { /* do something */ }

}

When it exports it has a unique signature. In NativeScript this is compiled down to ES5 code. So this will basically look like this when all said and done:

function HiYa() {} 

HiYa.prototype.hi = function() { /* do something */}

module.exports = HiYa;

Now when I do a const HiYaRunnerMaster = require('@nativescript/core/blah'); The Blah.js file is loaded into memory; parsed and then a runnable object representation of this is passed back to HiYaRunner, lets call this HiYaRunnerMaster. So far so good.

Now for the NativeScript tns-core-module redirects / compatibility layer.. This is the exact code that you see in tns-core-modules/* in 6.2... It basically redirects to the @nativescript/core version of the file. Seems simple, and unless you are paying close attention you might not even see why this actually is a problem.

function __export(m) {

    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];

}

Object.defineProperty(exports, "__esModule", { value: true });

__export(require("@nativescript/core/blah"));

So when I do a const HiYaRunnerFromTNS = require('tns-core-modules/blah'); it loads the above code and runs it. . It first defines the __export function, then runs that function on what it gets from the require of @nativescript/core/blah . Seems simple enough. Lets break this down into each step to what the v8/JavaScriptCore engines do.

  1. const tempObject = require('@nativescript/core/blah'); -- THIS object is the same/identical HiYaRunnerMasterObject as we got earlier when we ran it. Every time we do this specific require statement, we get the exact same object. Which is what we want!
  2. __export(tempObject); This runs the export function, passing in our HiYaRunnerMasterObject which so far doesn't seem like an issue.
  3. for (key in HiYaRunnerMasterObject) { we are looping through every property in HiYaRunnerMasterObject
  4. if (!exports.hasOwnProperty(key) verify the exports variable doesn't have this key property already.
  5. exports[key] = HiYaRunnerMasterObject[key]
  6. And behind the scenes exports is passed to the HiYaRunnerFromTNS at the end.

Did anyone else see what happened in step 5? First, exports is a 100% BRAND NEW OBJECT. (our Object #2). We then COPIED all the properties/functions from Object #1 (HiYaRunnerMasterObject) onto this new Object #2 (exports). Each copy is actually a new piece of memory that point to the old object's key value. So the new exports[Key] = old.value. in simplistic terms has two separate memory slots; "Key" and Value". So exports[key] is always a new memory pointer (of key) that either points to the original object's [key]; or to its own copy of a un-boxed object. So the memory usage could be 50% of the original object to the exact same memory usage as the original object, all depending on what the values (boxed or unboxed) are. In NativeScript it should be much closer to adding the 50% additional memory, not the 100% as a worst case number as almost all values on a nativescript key are objects.

At the end, in step 6 we used the brand new object to assign to HiYaRunnerFromTNS. Whoops; so we now have TWO different objects in memory with two different sets of memory usage.

This means that if you monkey patch the HiYaRunnerFromTNS copy; you are not actually monkey patching the actual original and running version. So in several of my plugins, this caused them to break as the monkey patching basically never occurred as far as the running copy of NativeScript was concerned.

In addition their is one more potential gotcha; I'll make a simple example:

let Obj1 = {a: {value: 1}}; // Original Object
let Obj2 = {a: Obj1.a}; // Copied key object

If I do Obj1.a.value = 2; then Obj2.a.value === 2 because both a's point to the exact same memory location with a "value" key in it . However, if I do Obj1.a = {value: 10} then Obj1.a then creates a brand NEW object which also has a value key; of which it has the value of 10 in it. And Obj2.a continues to point to the OLD original object with the value = 1. So even though your objects initially started pointing to the exact same value in memory. A new object assignment will replace just that objects location.

Why does this matter? When since the running instance of NativeScript is the @nativescript/core versions of the object. And your applications code will all currently require tns-core-module versions; you DO initially have two objects but they should all start out pointing to the same memory locations. Just like our example earlier (Obj1.a = Obj2.b) However, if you change a value that is NOT using a setter -- then the value you just changed won't be propagated to the running version, and you will have Obj1.a = {value: 10} and Obj2.a = {Value: 1} and you won't have a clue you just did a noop statement.

I do want to say the above potential gotcha scenario is very UNLIKELY, all code that I can think of runs through a setter; a setter function should be getting and setting the original object's value, and thus they shouldn't go out of sync. But corner cases can and do occur. So my recommendation for anyone updating to NS 6.2 is to do...

Recommendations

  1. Long term, replace ALL tns-core-modules/ with @nativescript/core/ and all nativescript-angular/ with @nativescript/angular/ everywhere in your app. No longer have ANYTHING pointing to tns-core-modules/ or nativescript-angular -- pretend those old namespaces no longer exist and were deleted.
  2. Update to the latest @proplugins if you are using any @proplugins, as most of them have already made the transition, as this will at least save you time on that aspect.

Short term tip (and highly recommended until they have a solid fix):

Richard Smith, posted a good tip that can quickly get you working -- you can add a rule to your `webpack.config.js` file. Find the alias section and add:

   
  "nativescript-angular": "@nativescript/angular",
  "tns-core-modules": "@nativescript/core",

So it looks like:

  
alias: {
      "~": appFullPath,
      "nativescript-angular": "@nativescript/angular",
      "tns-core-modules": "@nativescript/core"
},

NativeScript 5.4 Build Failures

People who have been using the plugins like Firebase and/or other Google services may have all of a sudden had their apps stop building with an error like this.

+ adding aar plugin dependency: .[some path].\widgets-release.aar
.[somepath].\app\src\main\AndroidManifest.xml:22:18-91 Error:
Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
Suggestion: add 'tools:replace="android:appComponentFactory"' to element at AndroidManifest.xml:19:5-37:19 to override.


FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':app:processDebugManifest'.
Manifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
Suggestion: add 'tools:replace="android:appComponentFactory"' to element at AndroidManifest.xml:19:5-37:19 to override.


Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
Get more help at https://help.gradle.org
BUILD FAILED in 30s

Wonderfully helpful error message huh? Well the issue is that Google recently changed their services to use AndroidX descended components. This change has been in the wings for a while and even NativeScript has been working towards this goal (NS 6.0 will be fully AndroidX). But in the meantime unfortunately Android.* and AndroidX.* are totally incompatible.

There are two things you might have to do to solve this so that you can continue to work...

  1. You create (or modify if it exists) a file in your App/App_Resources/Android/before-plugin.gradle and add the following section:
android {
     project.ext { 
          supportVersion = "28.+"
          googlePlayServicesVersion = "16.+"
     }
} 

This will pin your app to continue using a slightly older version of the Google services which is still based on the Android library. (IMPORTANT NOTE: when you later upgrade to NativeScript 6, you will want to delete this out; because NS 6 is Android X based; and so you will want the later versions).

2. If the above doesn't fully solve the issue; you may have to do a search in your node_modules and specifically the plugins for any plugin that actually uses google services in their gradle.include files; they might be hard coded to use a "+" as the version, and you will need to change them to be also "16.+"

--- UPDATE:

I had one of my friends, Dick Smith; showed me another solution via slack this morning; instead use the app.gradle file (also located in the same spot App/App_Resources/Android/app.gradle)

dependencies {
     configurations.all {
          exclude group: 'commons-logging', module: 'commons-logging'
         resolutionStrategy.eachDependency { DependencyResolveDetails details ->
             def requested = details.requested
             if (requested.group == 'com.google.firebase') {
               details.useVersion '17.+'
             }
             if (requested.group == 'com.google.android.gms') {
                 details.useVersion '16.+'
             }
             if (requested.group == 'com.android.support' && requested.name != 'multidex') {
                 // com.android.support major version should match buildToolsVersion
                 details.useVersion '28.+'
             }           
         }
     }
 }

This will override it as Gradle looks for the pieces it needs; it runs this code and even thought the plugins gradle file might state it wants version X of something; it will change it to the version we want... (Please note the same warning applies; when you upgrade to NS 6.0 in a couple months, you will want to then either change these versions to the AndroidX versions or delete this section).

--- End Update.

Finally after you make both those changes; I highly recommend you do a "tns platform clean android" before you rebuild the app, so everything is clean..

Finally if all else fails and you aren't expecting to release your app for another couple months; you can actually upgrade to the test version of NS 6.x; this version is currently unstable and you might have other issues (and you will probably want to update to the latest 6's each day just to try and make sure you are on the most recent version...)

npm i -g nativescript@next
npm i --save tns-core-modules@next
tns platform remove android
tns platform add android@next

If @next fails, and you need to return back to a solid release you can do the following:

npm i -g nativescript@latest
npm i --save tns-core-modules@latest
tns platform remove android
tns platform add android@latest

Please note; I do NOT recommend you run @next it normally is the least stable and most buggy version of Nativescript you can run. But sometimes you have to do what you have to do to proceed. 🙂

NativeScript 5.x and HMR Issues

If you are one of the lucky people who are reading this blog article, because your app used to work! After upgrading to the latest NativeScript and enabling the new Workflow your app is now broken...

Well I have some good news... It is easy to get back to a working state.

Here is how you can revert back to using the Legacy Workflow..

First thing, you need to open the nsconfig.json file in the root of your project. This file is where this key parameter is kept.

Inside the nsconfig.json file you will see "useLegacyWorkflow": true change this to false and save the file.

Then from the CLI; do a
- tns platform clean android (if using Android)
- tns platform clean ios (if using iOS)

Then you can do a tns run android or tns run ios and be back working.

Please note; DO NOT USE --bundle or --hmr flags; as this will re-enable HMR and the only reason you are changing this flag is because HMR is already not working for you...

I would recommend you submit a bug report to the nativescript-dev-webpack github repo about your issue; so that it can be tracked. Version 6.0 of NativeScript is supposedly going to disable the Legacy Workflow mode; so it is critical they get all the data they can on what issues you have seen so they can iron them all out...

Common HMR issues with fixes:

  • Your page/css/xml/html isn't changing on the device.
    • Make sure the name of the files end with "-page". So "main-page.xml" or "cool-page.css"
  • Your extra files (like sqlite databases) aren't being synced to the device.
    • Edit your webpack.config.js file, and add them to the copy step so that they are bundled up.

Some common broken NativeScript-Core HMR issues:

  • Pure JavaScript apps (i.e. not TypeScript) do not function properly on the second screen or on the first screen if using a SideDrawer as the root view. Click/Tap/Event handlers are not assigned properly.
  • Width/Height Specific files are unused (i.e. main-page.HW600.xml doesn't work; only main-page.xml is picked up.)
  • Shared NativeScript XML files do not link up to proper event handlers. (Similar to first issue; but a different aspect)
  • Some CSS files do not seem to be tracked properly.

NativeScript-Core replacing the root view...

Please note this to my knowledge only works with NativeScript-Core / PAN (Plain Awesome NativeScript) applications. I do not believe this will work with the Vue or Angular variations of NativeScript. There maybe another way to accomplish this with those variations, and if you know how -- please let me know and I'll update this post.

So I was minding my own business again, and I saw this question pop up in NativeScript GitHub. Take this scenario; you want to have a SideDrawer that is active on all pages.

Seems simple enough you create the /app-root.xml as this:

&lt;nsDrawer:RadSideDrawer xmlns:nsDrawer="nativescript-ui-sidedrawer"><br>    &lt;nsDrawer:RadSideDrawer.drawerContent><br>        &lt;GridLayout backgroundColor="#ffffff"><br>            &lt;Label text="Go to Cool Page" tap="page1"/><br>            &lt;Label text="Go to Another Cool Page" tap="page2"/><br>        &lt;/GridLayout><br>    &lt;/nsDrawer:RadSideDrawer.drawerContent><br>    &lt;nsDrawer:RadSideDrawer.mainContent><br>        &lt;Frame defaultPage="/pages/main/main-page">&lt;/Frame><br>    &lt;/nsDrawer:RadSideDrawer.mainContent><br>&lt;/nsDrawer:RadSideDrawer><br>

But now I need a login page... And my awesome side drawer is now active on the login page. Uh-oh -- it even has a ton of links that go to places and so we don't want that if they haven't even logged in. In fact we probably don't want them to even have access to the side drawer when not logged in...

What is the solution?

My solution, is fairly simple is you revert your code back to close to the default app-root.xml:
Your <strong>app-root.xml</strong> actually is changed to this:

<Frame defaultPage="/pages/login/login-page"/>

That is because we want the main frame of the app to load my login page; in this case my login page will force them to login if they don't already have a valid stored login token... Notice, their is no SideDrawer, because it is no longer the root.. So part one is working great, no side drawer during login...

So now how do we get our cool SideDrawer to be active on the rest of all the pages...

const Application = require('tns-core-modules/application');<br><br>function loginIsValid() {<br> <strong> Application._resetRootView("/pages/root/root");</strong><br>}<br>

Yep, look their -- there is a cool feature in the framework that allows you to REPLACE (or RESET) the root view. So by doing this, my /pages/root/root.xml file now contains the SideDrawer root layout which I displayed at the top of this post.

Notes:

My own personal applications typically are laid out in such a fashion that I have a "pages" folder, and in the "pages" folder I have each separate page js, CSS, and XML. This typically allows me to quickly find the page I want to work on.

NativeScript AndroidX support and Plugins

The next major version of NativeScript (version 6) will be switching from the android.support packages to the new androidx namespace. This will be a hard break; as you can only use the support api's or androidx api's; but not both. This is something Google has been implementing for a while; and NativeScript is getting on board to eliminate later issues and give us great support. The awesome developers at Progress have already created a fork of NativeScript; which you can install to test which uses androidx instead of using the support libraries.

Since one of my plugins is affected by the changes; I took a look into what was required to support both the current versions of NativeScript and the new upcoming version of NativeScript.

For any of you who have plugins; this code is what I basically devised.

let androidSupport=null;
if (android.support && android.support.v4) {
  androidSupport = android.support.v4;
} else if (global.androidx && global.androidx.core) {
  androidSupport = global.androidx.core;
}

Then you can use androidSupport to access the namespace, and it pretty much works the same as what you would access using android.support.v4 or the new androidx.core replacement. Because we used feature detection; this allows us to support both namespaces in the same code base.

So if you have JavaScript or TypeScript code that uses android.support.* then you can use this technique to make your plugin forwards compatible with the next major version of NativeScript while retaining support for the prior versions.

Here is a list of classses that are being moved from android support to androidx: https://developer.android.com/jetpack/androidx/migrate

VMWare Network Hickups ( sent link down event. )

I ran into this because I went to a hotel that had DHCP renew every 300 seconds, trying to download something inside the VM was getting clobbered...

If you check your syslog or kern.log and see the following entries; then I might have the solution for you:

kernel: [235397.022939] userif-3: sent link down event.
kernel: [235397.022943] userif-3: sent link up event.

Then the issue is one that when your DHCP renews it calls into the kernel with the new information; unfortunately this causes VMWare Workstation & Player (might effect other VMWare products?) to reset its network stack, even though nothing changed...

The solution after a lot of time googling different terms and trying to figure out what exactly was going on. I finally found it here (by Jan Just Keijer): https://www.nikhef.nl/~janjust/vmnet/ it was for an much older version of VMWare Player; but the solution still applies to VMWare workstation 15...

To make sure the information doesn't just disappear in the future (and so I can easily find it myself) I'm going to quote the relevant code and/or modifications I did..

Extract the /usr/lib/vmware/modules/source/vmnet-only.tar tar file, you need to modify the userif.c file. Search it for VNetUserIfSetUplinkState function and right after the variable declaration add if (!linkUp) return 0;

// Around line 966
VNetUserIfSetUplinkState(VNetPort *port, uint8 linkUp)
 {
    VNetUserIF *userIf;
    VNetJack *hubJack;
    VNet_LinkStateEvent event;
    int retval;

    /* ADD: never send link down events */   
    if (!linkUp) return 0;
    /* END Added code */
    
    ... rest of code ...

This will eliminate the linkdown events; which disconnect the internet from the VMWare network driver...

After you do that; recreate the vmnet-only.tar and replace the original. Then you can run: /usr/bin/vmware-modconfig --console --install-all

And then either
systemctl restart vmware
or
service vmware restart

If you still see " sent link down event." in your logs; then the patch wasn't applied properly.

NativeScript application wide runtime settings (updated 5.2)

NativeScript Switch

On thing most people are probably not aware of is that the Android runtimes has a large number of knobs and switches to customize certain behavior of the engine.   I will try and list out all the current switches and knobs that you can use.   iOS now has one switch.

First of all the settings go into the /app/package.json   If you open up the default package json you probably will see:

{   
    "android": {      
        "v8Flags": "--expose_gc"    
   },    
   "main": "app.js",
   "name": "tns-template-hello-world",
   "version": "5.2.0"
}

That "android" key is where everything goes for Android specific settings.   The default setting that comes in the files is "v8Flags": "--expose_gc" this one is rather critical in that this enabled the runtime to call gc from JavaScript.   Now, there is quite a few v8Flags you can use; most the time they aren't needed but if you need to tune the v8 engine; you can add more flags into this string.  I even list a few in this post.   Let me warn you again; DO NOT remove the "--expose_gc".   You will cause your app to crash when something attempts to call the GC from code.

As of version 5.2; there is a "ios" key called "jscFlags" option that is now available. My recommendation while building/testing your app is to add the key "ios": { "jscFlags":"--validateOptions=1" } The jscFlags has items that can be used to tweak the JavaScript core engine. You can find more flags here.

Ok, so here is the complete list of settings you can change in the Android section:

SettingDefaultMin VersionDescription
gcThrottleTime03.xAllows you to throttle
how frequently JS GC
triggers native GC
See:
Memory Management
memoryCheckInterval03.xHow frequently to check
memory usage
See:
Memory Management
freeMemoryRatio0.03.xRation of memory before a GC is ran (works with
memoryCheckInterval)
See:
Memory Management
markingModefull3.xSwitches which type of GC engine to use.  "none" will enable a different engine which doesn't do
marking.  Typically faster
for Angular apps
handleTimeZoneChangesfalse4.1True will enable the
ability for a time zone
change in the settings to
effect the NS app.
maxLogcatObjectSize10244.0How many lines of text a
console output will log.
forceLogfalse4.0True will enable logging
in release mode
suppressCallJSMethodExceptionfalse5.1True will suppress the
boolean type conversion error from null that could randomly occur and crash the application.
codeCachefalse2.xTrue - Enables code
caching in the v8 engine.
v8Flags--expose_gc1.xTunes the v8 engine
heapSnapshotBlob""2.x(Believe to be unused
now)
heapSnapshotScript""2.x(Believe to be unused
now)
snapshot.blob""2.x(Believe to be unused
now)
profilerOutputDirKey""2.x(Believe to be unused
now)
profiling""2.x(Believe to be unused
now)

IOS Level Flags:

SettingDefaultMin VersionDescription
gcThrottleTime05.2Allows you to throttle
how frequently JS GC
triggers native GC
See: Custom Flags
memoryCheckInterval05.2How frequently to check memory usage
See: Custom Flags
freeMemoryRatio0.05.2Ration of memory before a GC is ran (works with
memoryCheckInterval)
See: Custom Flags
jscFlags""5.2Tunes the JavaScriptCore engine. Valid Flags

Top Level Global Flags:

SettingDefaultMin VersionDescription
discardUncaughtJsExceptionsfalse4.2True - will disable
crashing on JS errors, will
log any issues to console.

Recommended changes:
discardUncaughtJsExceptions: "true" - This will keep the app from crashing on certain types of errors; this should be enabled for release mode! Some developers might find it more productive to have this enabled while coding to eliminate application crashes. Please note you MUST pay attention to the console if you enable this during development; because most JS errors that would crash out; will now be just logged to the console.

suppressCallJSMethodExceptions: "true" - This will keep the app from crashing on the boolean conversion issue that can occasionally occur.  This is not to say the app might have some other issue later; but this will eliminate a stupid (imho) crash reason.

markingMode: "none" - This will enable the experimental GC engine, this will help incredibly with Angular based apps. Please note it "might" cause more crashing errors if a plugin forgot to hold a reference to something it needed later. However with the above discardUncaughtJsExceptions enabled; those errors won't crash the app.

Late Breaking news; in 4.2 they actually moved "discardUncaughtJsExceptions" to the top level of the package; the reason why is because shortly after they added the support to Android; the support was added to iOS.  So now this flag controls both iOS and Android, so it no longer belongs just in the "android" block.

So in your package json would look like this if you used my recommended settings:

{
   "android": {
       "v8Flags": "--expose_gc",
       "markingMode": "none",
       "suppressCallKSMethodExceptions": "true"
    },
    "ios": {
        "jscFlags": "--validateOptions=1"
    },
    "discardUncaughtJsExceptions": "true",
    "main": "app.js",
    "name": "tns-template-hello-world",
    "version": "5.2.0"
}

NativeScript iOS Delegates and easy access to modifications.

UPDATE: After a lot of direct testing; this technique does NOT work for fixing and/or updating iOS delegates.   NativeScript does NOT follow the JavaScript specs in this area.  (in other words NativeScript is broken/buggy here, but I do not expect to see a fix for it.)  So even though the JS engine updates the code properly and the delegate object on the JS side of the bridge looks like it is updated.  The original delegate that was first created is cached and used from that point forward.   This technique is valid for any other normal JavaScript stuff; but does not work for the iOS NativeScript delegates as I had expected it to.   Sorry for the bad information, everything would/should have worked if NativeScript didn't introduce bugs into the JS engine in this area on iOS.

---

Over a year ago, I had a long discussion with the NativeScript team about opening the delegates to allow the developers to easily access and change them before they are created.   Unfortunately a couple people on the NativeScript team disagreed with me and my reasoning and rejected my pull request that would have allowed easily extending the delegates before they are created.    They unfortunately thought it was "unsafe", and that was that, the delegate doors were shut.

So then for any projects I needed to extend a delegate I pretty much had to use a custom version of the NativeScript Core Modules for my clients. This has bit me a couple times; when I upgraded as then my changes were wiped out.  (Tip: Always list any of these type changes in your project readme, with code!)

Also, I never had the time to finish my planned work around "TNS-Core-Patcher"; which would have allowed plugins to have patch files to patch the NativeScript core modules during the prepare phase and include any code like delegate changes.     The TCP project is only partially done, so this post isn't about that cool project, that still might see the light of day when I have that mythical spare time.

But instead it is about a post about a issue that Gheric posted to the ios-runtime repo yesterday.   I originally responded and said my usual spiel about about how it only works if you can get access to the delegates prototype chain and that the NS team had dismissed allowing it on this issue.    He responded back and said he didn't see anything in my examples that couldn't be done using his proposed feature.

At first I was thinking, he had no idea what he was talking about.   But I am so happily wrong!    The current version of NativeScript uses this._delegate assignment inside the class for almost every single instantiated delegate that I can see.  I just quickly searched through the code and as far as I can tell; I can get access to every single created delegate.  This is what I believe Gheric was pointing out that the underlying instantiated delegate is accessible.  If he wasn't pointing to it; then because of his persistence; he made me go look at it again and then I discovered it.  But either way; it was because of Gheric's post that I understood the ramifications of having access to the instantiated delegates.

So lets add to to that, NativeScript's current engines now support a large chunk of ES6, not full ES6, but a large enough chunk where what I need exists now.

So what does that give me?   Well, it just gave me the entire ios delegate world!!!   Delegates are no longer locked behind an artificial barrier, and I can change them however and whenever I want!!!  Can you tell I'm excited?    I just got a second Christmas!

I now have a technique that totally eliminates the stupid door the team closed. So instead of letting me have a easy door to make my changes; I have found a awesome open window to allow me to make the exact same changes.

(c) 2013,Angelo DeSantis - https://www.flickr.com/photos/angeloangelo/8607844680/

Ok, so lets get to the nuts and bolts of how to do this.  The key is the ES6 Object.getPrototypeOf()function.  This allows us to get the prototype of an already instantiated object.   So it is almost as good as my original pull request.  In my original pull request I would be able to

let x = require('someComponent');
x.exportedDelegate.prototype.addFunction = function() {
/* Do whatever iOS expects this delegate function to do */
}

Very simple, and allowed me access to all sorts of things iOS expects you to be able to do when you want certain customizations.   Doing it off the prototype chain of the exported delegate is the easiest and BEST place to do it.   It doesn't matter if the delegate is instantiated or not.

Now the WORKING technique is:

let originalPrototype = Object.getPrototypeOf(someClass._delegate);
originalPrototype.addFunction = function() {
/* do whatever iOS expects this function to do */
}

Not that much harder, and the end result is identical.   However there is one potential gotcha with this new technique; if the delegate has not been instantiated yet -- you obviously can't get access to its prototype chain.  Depending on which delegate you need to modify you might have to wrap the code in a looping setTimeout() until the delegate is instantiated.

function fixPrototype(classObject, delegateName, delegateFunctionName, delegateFunction) {
if (!classObject[delegateName]) {
setTimeout(() => { fixPrototype(classObject, delegateName, delegateFunctionName, delegateFunction, 100);
return;
}
let prototypeOf = Object.getPrototypeOf(classObject[delegateName]);
protoypeOf[delegateFunctionName] = delegateFunction;
}

/* Call it */
fixPrototype(someClass, "_delegate", "addFunction", () => { /* do whatever ios expect */ });

 

This simple function, will allow you to pass in the object class, then the delegate name (most the time "_delegate" and then the function name you want to add, and then the function itself.   It takes care of the rest.

Remember, once you have patched a delegate prototype; ALL delegates using that prototype (including any of them created in the future) will get your changes.  That is why it is so important to patch the delegate prototype, you only need to do it once and it works everywhere.

NativeScript and Console logging on iOS (including XCode 9)

Been a while since I posted; and most this info was going to be presented at the NativeScript Developer days speech that we ran out of time in our cool session.     So I'm going to present it here, because I think this will help a lot of people.

First of all, NativeScript has an issue (at least on my machine) where the logging is incomplete and missing important things, like crash reports (and now on XCode 9, non-existent).

Well, when I ran into the two first issues using XCode 8.2 & 8.3.   I figured out a work around, because I hate my tools not working.    🙂

There is a really awesome set of utilities called libimobiledevice this set of utilities lets you do all sorts of things with a real iphone when it is connected to your computer including reading the logs.

 

Real Devices (any version of iOS)

So I typically do idevicesyslog | grep CONSOLE and it will then show me all my logs for the device and it basically fixes any issues other than some exception reporting.   Most the time my app doesn't crash so that command above I use probably 99% of the time.    However in the cases I need to see the NativeScript actual crash logs.   I use the above command and it will spit out the PID of your app next to the CONSOLE word.  I then cancel it, and do a idevicesyslog | grep <PIDID> and this gives me the full log including anything the NativeScript runtimes print.

Please note this only works for real devices.

Simulators (iOS 10 and before)

UPDATE for iOS 11 simulators, please see below!

Now for simulators; the process is very similar.

You run xcrun simctl list | grep Booted and it will give you a line like this:
iPhone X (4701B6F6-0EE4-423F-B5E2-DE1B5A8C32AC) (Booted)

Do you see that big old long uuid; you need that.

tail -f ~/Library/Logs/CoreSimulator/4701B6F6-0EE4-423F-B5E2-DE1B5A8C32AC/system.log | grep CONSOLE

This allows you to grep the log from that specific simulator.   Again, using the grep CONSOLE filter you can limit it to any console logs.   And you can also use grep <PIDID> to filter it to any application if you need the filtered down to, if you are needing the specific NativeScript application level logging or crash reports too.

 

Simulators (iOS 11)

iOS 11, changed the logging for the Simulator drastically.  Technically iOS 10 changes the NSLog drastically, but 11 enforced some new rules.   So iOS 11 broke the NativeScript internal logging; but it broke the cool tail trick above.    Everything now is now running through the new os_log facility.  The easiest way to get logging from your simulator is this single line:

log stream --level debug --predicate 'senderImagePath contains "NativeScript"'
--style syslog

Please note this is case sensitive, if you don't case the senderImagePath and NativeScript you won't have your filtered logging (or any logging at all if you type it wrong).   This part of it filters it down to just any NativeScript logging from all emulators running.

I created a simple bash script called /usr/bin/local/tnslog and dropped the above line it in and then chmod +x /usr/local/bin/tnslog 'd it.  So I can easily just start tns, and then open another terminal tab; and type tnslog and have all my logging again.

 

Installation

The simulator you need no extra tools.   For real devices you need the libimobiledevice toolset, which you can easily install via brew with the following two commands:

brew install --HEAD usbmuxd

and

brew install --HEAD libimobiledevice

It is really important that you install from head, the changes in Xcode 9 and iOS 11 have made a couple changes to both those libraries require some updates and the normal brew packages are out of date and doesn't have it.  So you need the latest and greatest.

 

Final Thoughts

I have been lazy, and just documented the steps.   If someone has some spare time and wants to create a quick bash script to automate the grabbing of the simulator id, and starting the older logging; against that simulator that would be cool to add to our tools.  But since I'm using primarily iOS 11 emulators now; my tnslog script handles all logging until the NativeScript team can fix their tool's issues.

 

NativeScript: @Next- should I use it?

Short version: Nope...   😉

Long Version:

I see this pop up frequently in the bug tracker and slack where someone is complaining about a issue; then we all find out it is a issue in the @next version.    Unfortunately Telerik really did everyone a huge disservice when they named it @next.

This is not actually the next version of that specific repo/codebase.     What, you say???  But it is named next.   Yes, I'm sorry to say it is totally a mis-named tag.   None of the repos have an actualNEXT branch.

The @next tag is actually tied to the MASTER branch.    And Master is not a RELEASE branch in NativeScript.    Master is used for testing, and validating things.  Once they have been validated and the feature(s), or fixes approved;  then that single item will be tracked and eventually moved into the Release branch before the real next upgrade release.

In the meantime if you decide to try @next, you will have many features/bugs that are still in testing or transition that might not even be 100% complete or working properly.     Now the Telerik devs have been trying really hard to keep the Master branch in a usable state (Kudos for the guys putting "DO NOT MERGE" on really major pull requests that are still being developed).   But even with them being careful; Master is still a DEVELOPMENT branch, so just like my own DEVELOPMENT branches, thing will slip through.   Or a design change will occur; where we decide to refactor something and then the current "master" is broken in some weird way that we didn't anticipate.   In addition there will be many features/bugs that are present on Master that will never be in Release in that form.    So, @next != release or really even release quality.

Please if you don't need to upgrade to @next; don't do it.  Not only will you stay saner; you won't be wasting peoples time with issues that are frequently meaningless.   @Next is really so that you can test if a bug is fixed; but be very leary of using it for ANY other reason, the rest of the non-release changes in @next will probably bite you...

However I would ask that if a bug you reported is fixed on @next; please upgrade temporarily to verify the bug is fixed, and report back to the team.   This is helpful for them to know they squashed your bug as you see it.   But once you are done,testing; I would highly recommend you go back to the @latest tag.   😉

To upgrade you do npm i tns-core-modules@next  (Example sake; we are assuming it is the core you are upgrading, but all the repos have a @next).   Then a tns platform clean android or ios.  Then re-build and run the app.    When you are done testing, change the @next to @latest, and you will get the latest RELEASE version available for that repo.