Chrome DevTools Debugging
Overview
The following article is an overview of the features supported in Chrome DevTools when debugging NativeScript applications.
Table of supported features as of NativeScript version 3.4.0
Android Chrome DevTools | iOS Safari AppInspector | iOS Chrome DevTools | VSCode Extension | |
---|---|---|---|---|
Debugger | ✔ | ✔ | ✔ | ✔ |
Console | ✔ | ✔ | ✔ | ✔ |
Resources | ✔ | ✔ | ✔ | not applicable |
Network | ✔ | ✔ | ✔ | not applicable |
Elements (DOM) | ✔ | ✘ | ✔ | not applicable |
Elements (Styles) | ✘ | ✘ | ✘ | not applicable |
Memory Profiling | ✘ | ✘ | ✘ | not applicable |
Timeline and CPU Profiling | ✘ | ✔ | ✘ | not applicable |
Debug CLI Builds
To debug on Android execute the following command:
tns debug android
To debug on iOS execute the following command:
tns debug ios
For security reasons, the generated debugging agent can't be started automatically from the command-line. That's why NativeScript CLI generates a URL which is printed on the screen instead.
To start debugging, open the following URL in Chrome:
chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:40000
You need to manually copy it in Google Chrome's address bar to start debugging.
Debugger
Very often you need to reproduce a bug many times to get to the root of the problem. The debugger feature can help you find and diagnose the bugs occurring at runtime using the following techniques:
-
Pause code with breakpoints - Set a breakpoint so that you can pause your code in the middle of its execution. Once your code is paused (Figure 1), you can step through it (Figure 2) to investigate the control flow and property values
Figure 1: A line-of-code breakpoint set on line 29:
Figure 2: Stepping over code:
Note: Sometimes when the code you want to debug executes too early, for example during application initialization, you will have to place a Line-of-code breakpoint - 'debugger;' statement in your code and start the debugging process with the
--debug-brk
CLI flag to allow the DevTools enough time to connect and attach before your code is executed. -
Inspect local and global properties, and variables - While paused on a line of code, use the Scope pane (Figure 3) to view and edit the values of properties and variables in the local, closure, and global scopes.
Figure 3: The Scope pane:
Note: The
Global
object in the context of a NativeScript application is a custom object and contains only a limited subset of theWindow
global object present in a Web application. -
Watch the values of custom JavaScript expressions - Use the Watch pane (Figure 4) to watch the values of custom expressions. You can watch any valid JavaScript expression.
Figure 4: The Watch pane:
-
View the current call stack - While paused on a line of code, use the Call Stack pane (Figure 5) to view the call stack that got you to this point. Click on an entry to jump to the line of code where that function was called. The blue arrow icon represents which function DevTools is currently highlighting.
Figure 5: Call stack:
Note: To be able to debug code other than JavaScript, the transpiled sources should include inlined source maps for your code (default when developing NativeScript apps with TypeScript).
Console
The console complements the debugging experience in that it displays messages output from your code, allows you to execute arbitrary JavaScript expressions in the context of the NativeScript runtime, and even save the log to a file. The console feature consists of the following functionalities to make your debugging experience more smooth:
Writing to the console - Use the
console.log()
method for any basic logging to the console.-
Autocompleting commands and expressions - When you type in the Console, the Console automatically displays an autocomplete dropdown menu (Figure 6) of relevant methods that match the text that you have already typed. This includes previous commands that you executed.
Figure 6: Console autocomplete:
Measure execution times - The
time()
method starts a new timer and is very useful to measure how long something took. Pass a string to the method to give the marker a name. When you want to stop the timer, calltimeEnd()
and pass it the same string passed to the initializer. The console then logs the label and time elapsed when thetimeEnd()
method fires.Evaluate expressions - Explore the state of any object of your global application scope, or the paused local scope from the Console by evaluating an expression just by typing it.
Resources
Scripts loaded by the JavaScript Virtual Machine appear in the Sources panel, which you can then debug and place breakpoints in. Besides scripts all other text and image resources found in your application are also listed in the Sources panel, grouped by folder name by default. You can inspect and search inside the contents of XML, HTML, CSS, JSON, and image files. Text and image network responses are also stored there.
Network
DevTools shows all* network requests in the Network panel while the DevTools are open. In the panel you will find information about the requests made, whether they've completed, the response status and data.
-
View a log of requests - Use the Requests table (Figure 7) to view a log of all requests made while DevTools has been open. Clicking or hovering over requests reveals more information about them.
Figure 7: The Requests table, outlined in blue:
Note: Time, Size, and Waterfall metrics may sometimes appear incorrectly or be missing altogether if a Status Code is available, however, that means a response has been received.
-
View a preview of a response body - To view a preview of a response body: Click the URL of the request, Under the Name column of the Requests table. Click the Preview tab (Figure 8). This tab is mostly useful for viewing images.
Figure 8: The Preview tab, outlined in blue:
-
View a response body - To view the response body to a request: Click the URL of the request, under the Name column of the Requests table. Click the Response tab (Figure 9).
Figure 9: The Response tab, outlined in blue:
-
View HTTP headers - To view HTTP header data about a request: Click the URL of the request, under the Name column of the Requests table. Click the Headers tab (Figure 10).
Figure 10: The Headers tab, outlined in blue:
-
View query string parameters - To view the query string parameters of a URL in a human-readable format: Open the Headers tab for the request you're interested in. Go to the Query String Parameters section.
Note: *This is currently available for the built-in http module. For third-party modules that do network requests, additional code must be implemented to populate the Network Tab. See Plugin author's guide for details on how to do it for your plugin.
Elements
The Elements panel in DevTools displays information about the current view tree, the attributes of each child, and its computed styles.
-
Inspect the view tree - The DOM tree view (Figure 11) shows the current state of the tree; it may not match the XML that was originally loaded for different reasons.
Figure 11: The DOM tree view of a NativeScript application:
View node modifications - Changes made to the view tree through code will be reflected immediately in the Elements panel. Adding, removing child views, navigating to another page, or changing view properties are some of the events that trigger an update.
-
Live edit attributes - Examine and add, edit, or remove the current element's attributes.
To add a new attribute: Right-click the element. Select add attribute. Set the attribute as a text:
my-custom-attr="42"
. Press Enter.To remove an attribute: Click the element. Double-click the attribute text, select the text value, and press Backspace/Delete.
To change the value of an attribute: Click the element. Hover over an attribute and right-click it. Select "Edit attribute", edit accordingly, and press Enter.
Delete a child node - To delete a DOM node: Open the more (
...
to the left of a selected DOM node) actions menu and select Delete Node, or press the Delete key.
Plugin author’s guide
Writing plugins is a great way to give back to the community by making application development ever easier by abstracting complex logic through a simple interface. What is even better is when your plugin can integrate almost seamlessly with the expanding arsenal of debugging tools provided by the platform. Following are the optional requirements and interfaces your plugin should comply to, to have your plugin's components/data shown in the respective DevTools panels.
-
Elements panel (UI plugins) - The following content concerns only plugin authors who wrap and expose native Android/iOS views in their work. If you are a plugin author or plan to be one, you can either:
A: start off with a nativescript plugin template, which provides you with an already well-established structure to wrap native UI views in. To get started head over to the official seed's repository and follow the README instructions - https://github.com/NativeScript/nativescript-plugin-seed
B: extend the
tns-core-modules
's View base class. Detailed information and tutorial on doing that coming soon!
-
Network requests in plugins - Note: The following content concerns only plugin authors who wrap and expose Android (Network agent in DevTools not yet supported with a public API in the iOS runtime) http functionalities. To make your http functionality debuggable, there are callbacks you need to call at certain times of the lifecycle of the network request, following a specific protocol. For your convenience,In order to we've exposed callbacks and TypeScript interfaces to facilitate sending information to the Network agent.
-
Immediately before making the request:
Check if the
global.__inspector
object is available, and whether the DevTools are connected:if (global.__inspector && global.__inspector.isConnected) { .. }
Build a RequestData-compliant object, as declared in the
debugger module
.RequestData
contains the minimum subset of properties needed to display request entries in the Network panel. Finally call to the runtime-exposed callback:global.__inspector.requestWillBeSent(requestData);
-
When a response is received:
Check if the
global.__inspector
object is available, and whether the DevTools are connected, as shown above. Build a ResponseData-compliant object, as declared in thedebugger module
.ResponseData
contains the minimum subset of properties needed to display the response for a completed request.Build a LoadingFinishedData-compliant object, as declared in the
debugger module
. The object notifies the Network agent that a request has completed, as well as the time spent.Build a SuccessfulRequestData-compliant object, as declared in the
debugger module
. The object contains the response data, in a string format, the Id of the original request the response data corresponds to, and information whether the content should be base64-encoded, or not.Finally call the following runtime-exposed callbacks:
global.__inspector.responseReceived(responseData); global.__inspector.loadingFinished({ requestId: requestIdStr, timestamp: getTimeStamp() }); global.__inspector.dataForRequestId(successfulRequestData);
-
Debugging typescript-transpiledTo debug your TypeScript plugin based on the sources, and not the transpiled JS, it is enough to edit the respective
tsconfig.json
to output sources with inlined maps. That will ensure that the TypeScript sources will also show in the Sources pane, and allow you to debug it. Don't forget to transpile the sources without source maps before publishing the plugin.
Credits
Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.