<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: TJ VanToll</title>
    <description>The latest articles on Forem by TJ VanToll (@tjvantoll).</description>
    <link>https://forem.com/tjvantoll</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F96627%2F05af3168-fd38-4d42-9ce7-4bdae28eab88.jpg</url>
      <title>Forem: TJ VanToll</title>
      <link>https://forem.com/tjvantoll</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tjvantoll"/>
    <language>en</language>
    <item>
      <title>Establishing a Robust MLOps Workflow for TinyML</title>
      <dc:creator>TJ VanToll</dc:creator>
      <pubDate>Tue, 26 Sep 2023 17:37:10 +0000</pubDate>
      <link>https://forem.com/blues/establishing-a-robust-mlops-workflow-for-tinyml-4oo3</link>
      <guid>https://forem.com/blues/establishing-a-robust-mlops-workflow-for-tinyml-4oo3</guid>
      <description>&lt;p&gt;A machine learning model is only as good as the data it’s trained on. Gathering a robust set of training data is challenging for any ML project, but it’s especially difficult in the world of TinyML, where devices are often underpowered and running in areas with limited connectivity.&lt;/p&gt;

&lt;p&gt;Here at Blues we specializing in making connectivity easy for embedded projects, and together with our friends at &lt;a href="https://edgeimpulse.com/"&gt;Edge Impulse&lt;/a&gt; and &lt;a href="https://zephyrproject.org/"&gt;Zephyr&lt;/a&gt;, we’ve put together a workflow that we think helps make MLOps—or the process of continuously improving an ML model—possible for tiny devices.&lt;/p&gt;

&lt;p&gt;In this article I’m going to lay out an opinionated way to implement a robust MLOps process. We’ll start by looking at how to collect ML data from your remote devices, and then show how you can update ML models running on our devices in the field. At the end you’ll have a workflow that looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y6IND9vK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5yz2fnzjq5uzhajpgb4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y6IND9vK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5yz2fnzjq5uzhajpgb4.gif" alt="A full process of ingesting ML data and posting it to Edge Impulse" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: See &lt;a href="https://www.youtube.com/watch?v=7cLzNz0Sa-Y"&gt;&lt;em&gt;Optimized MLOps with Edge Impulse, Blues, and Zephyr&lt;/em&gt;&lt;/a&gt; on YouTube for a video version of this tutorial.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Collecting ML Data
&lt;/h2&gt;

&lt;p&gt;For this article we’re going to assume we manage a fleet of devices tasked with monitoring industrial equipment. Each device is fitted with an accelerometer that measures machine vibrations, and runs those readings through an ML model that has been trained to detect abnormalities.&lt;/p&gt;

&lt;p&gt;To adopt an MLOps workflow, this project needs a way to gather accelerometer samples from the field, and a way to send those samples to the cloud so that they can be used to train an updated model.&lt;/p&gt;

&lt;p&gt;To send the data we’re going to use the &lt;a href="https://blues.io/products/notecard/"&gt;Blues Notecard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blues.io/products/notecard/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oNr-9f-n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqva5sj1nlqqe1o6thhz.png" alt="The Blues Notecard" width="200" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Notecard is a system on module that makes connectivity simple for embedded projects. Specifically, the Notecard offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;500MB of cellular connectivity&lt;/li&gt;
&lt;li&gt;Global cellular over LTE-M, NB-IoT, or Cat-1&lt;/li&gt;
&lt;li&gt;Secure “off the internet” communications from device-to-cloud&lt;/li&gt;
&lt;li&gt;Low-power hardware (~8µA when idle), and power-conscious firmware&lt;/li&gt;
&lt;li&gt;Easy embedding with onboard M.2 Key E connector or via a companion board&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Notecard is great for connectivity on remote devices as it can work in areas without Wi-Fi coverage, and can work on equipment that moves between locations or on vehicles. You can even &lt;a href="https://www.youtube.com/watch?v=MVYVVkgdDC4"&gt;use the Notecard for asset tracking&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To communicate with the Notecard in our project we need a few other pieces of hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up your hardware
&lt;/h3&gt;

&lt;p&gt;The easiest way to get started with the Notecard is with a &lt;a href="https://shop.blues.io/collections/blues-starter-kits"&gt;Blues Starter Kit&lt;/a&gt;. The Starter Kit comes with a development board (Notecarrier), as well as a &lt;a href="https://blues.io/products/swan/"&gt;Swan microcontroller&lt;/a&gt; that can run this project’s firmware.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2QTUCVjV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/363x127owravhk0jsqvn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2QTUCVjV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/363x127owravhk0jsqvn.jpg" alt="A Blues starter kit all connected" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;a href="https://dev.blues.io/quickstart/blues-quickstart/"&gt;Blues quickstart&lt;/a&gt; has full details on how to assemble a Blues Starter Kit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the Starter Kit assembled, the last thing we need is an accelerometer to use to take readings. For that accelerometer we’ll use an &lt;a href="https://www.adafruit.com/product/2809"&gt;LIS3DH&lt;/a&gt;, which you can connect to the Starter Kit’s Notecarrier via a Qwiic cable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dps0us2n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf9h5xvgmiwr24h2z6iy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dps0us2n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf9h5xvgmiwr24h2z6iy.jpg" alt="The full hardware set up of this MLOps project" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With all hardware assembled, let’s look at the firmware we’ll use to gather the accelerometer readings.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Firmware
&lt;/h3&gt;

&lt;p&gt;This project’s firmware is responsible for gathering readings from the accelerometer, and using the Notecard to send those readings to the cloud.&lt;/p&gt;

&lt;p&gt;For the firmware implementation we’re using &lt;a href="https://zephyrproject.org/"&gt;Zephyr&lt;/a&gt;, as Zephyr is low-level enough to give us access to features like &lt;a href="https://docs.zephyrproject.org/latest/kernel/services/threads/index.html"&gt;threading&lt;/a&gt;, while high-level enough to provide &lt;a href="https://docs.zephyrproject.org/latest/hardware/peripherals/sensor.html"&gt;sensor APIs&lt;/a&gt; and other development conveniences. Plus, Zephyr firmware runs great on the STM32-based boards like the Swan, and the Notecard has a &lt;a href="https://dev.blues.io/tools-and-sdks/firmware-libraries/zephyr-sdk/"&gt;ready-to-use Zephyr SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The full firmware source code for this project is &lt;a href="https://github.com/tjvantoll/mlops-zephyr/"&gt;available on GitHub&lt;/a&gt;, and I will highlight some of the most important parts here.&lt;/p&gt;

&lt;p&gt;First, the firmware has to establish a connection between the Notecard and the Notecard’s backing cloud service, &lt;a href="https://notehub.io"&gt;Notehub&lt;/a&gt;. It does so by performing a &lt;a href="https://dev.blues.io/api-reference/notecard-api/hub-requests/#hub-set"&gt;&lt;code&gt;hub.set&lt;/code&gt; request&lt;/a&gt; on the Notecard, and sending a &lt;a href="https://dev.blues.io/api-reference/glossary/#productuid"&gt;ProductUID&lt;/a&gt; that tells what project to associate this Notecard with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NoteNewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hub.set"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com.your-company:your-product-name"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"continuous"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"sn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"your-device-name"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;NoteRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple of notes on the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All of the Notecard’s API use JSON. The Notecard’s C SDK includes a number of helper functions such as &lt;code&gt;JAddStringToObject&lt;/code&gt; to make it easier to create JSON objects and send them to the Notecard.&lt;/li&gt;
&lt;li&gt;The development board that comes in a Blues Starter Kit, the Notecarrier F, performs all the necessary wiring so that the Swan can communicate with the Notecard via I2C. The &lt;code&gt;NoteRequest&lt;/code&gt; API performs this communication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a connection with Notehub established we next need to gather data from the accelerometer. The Zephyr code to take a reading from the LIS3DH is relatively straightforward on its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;sensor_value&lt;/span&gt; &lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_sample_fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to fetch sensor data (error: %d)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_channel_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SENSOR_CHAN_ACCEL_XYZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to get sensor data (error: %d)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_value_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_value_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_value_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reading - x: %f, y: %f, z: %f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet takes a single accelerometer reading and places the values in three floating point variables named &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;z&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For this example we need code that is a little more complex though, as we’re not taking one accelerometer reading—instead, we’re taking a series of readings over a period of time so that it can be sent to an ML model to look for patterns.&lt;/p&gt;

&lt;p&gt;For this project we’re using &lt;a href="https://edgeimpulse.com/"&gt;Edge Impulse&lt;/a&gt; to create and train our models. After you’ve &lt;a href="https://dev.blues.io/guides-and-tutorials/building-edge-ml-applications/blues-swan/"&gt;built a model with Edge Impulse&lt;/a&gt;, Edge Impulse lets you download an SDK with sample code that helps you gather sensor data at the intervals their platform expects for training purposes. Here’s what that ends up looking like in our Zephyr code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;sensor_value&lt;/span&gt; &lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Determine the next tick (and then sleep later)&lt;/span&gt;
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;next_tick_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NoteGetMs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;EI_CLASSIFIER_INTERVAL_MS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_sample_fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to fetch sensor data (error: %d)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_channel_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SENSOR_CHAN_ACCEL_XYZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to get sensor data (error: %d)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_value_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_value_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_value_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;accelerometerData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x: %f, y: %f, z: %f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_tick_ms&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;NoteGetMs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;NoteDelayMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The various constants in this sample, &lt;code&gt;EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE&lt;/code&gt;, &lt;code&gt;EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME&lt;/code&gt;, and &lt;code&gt;EI_CLASSIFIER_INTERVAL_MS&lt;/code&gt;, help dictate exactly how often the code takes accelerometer samples and loads them into the &lt;code&gt;buffer&lt;/code&gt;. If you run the code and look at the logs you’ll see the accelerometer samples logged live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P7mWGKq5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/374tdep41du2dmnt1n7c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P7mWGKq5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/374tdep41du2dmnt1n7c.gif" alt="Stream of accelerometer readings from the firmware" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the data loaded into a &lt;code&gt;buffer&lt;/code&gt; variable, our firmware’s last task is to send that data to Notehub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending the Data
&lt;/h3&gt;

&lt;p&gt;Large accelerometer samples like we’re using in this project require a decent amount of storage, and historically, working with these larger data packets was problematic for the Notecard, as the Notecard is primarily a low-bandwidth and low-power friendly device.&lt;/p&gt;

&lt;p&gt;However, the new Notecard &lt;a href="https://dev.blues.io/notecard/notecard-firmware-updates"&gt;v5.3.1 firmware release&lt;/a&gt; includes a new feature aimed at drastically speeding up data transfer for these types of transfers, and allowing you to upload up to 128kb at a time—which is plenty for our situation.&lt;/p&gt;

&lt;p&gt;The new Notecard APIs are named &lt;code&gt;card.binary&lt;/code&gt;, and the way you use them in Zephyr apps is with the &lt;code&gt;NoteBinaryStoreTransmit&lt;/code&gt; and &lt;code&gt;NoteBinaryStoreReceive&lt;/code&gt; methods. Here’s what that looks like in our sample.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;NoteBinaryStoreTransmit&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE_COBS&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;NoteBinaryStoreTransmit&lt;/code&gt; method loads the buffer into a dedicated area of the Notecard’s memory. Once loaded, you can send the data to your Notecard using the &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/web-transactions/"&gt;Notecard’s &lt;code&gt;web.post&lt;/code&gt; method&lt;/a&gt; with the &lt;code&gt;binary&lt;/code&gt; flag set to &lt;code&gt;true&lt;/code&gt;. Here’s what that looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NoteNewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"web.post"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ingest"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binary/octet-stream"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddBoolToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;NoteRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point our code is now sending batches of accelerometer readings to Notehub as a binary stream of floating-point values. Our final task is to write an endpoint that can accept that stream, parse out the floating-point values, and send them to Edge Impulse as new training data for our ML model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting the Data to Edge Impulse
&lt;/h3&gt;

&lt;p&gt;In this step we’re going to look at how to create an HTTP server that can accept data from Notehub and forward it to Edge Impulse. The example code I’m providing is written in Node.js, &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt;, and JavaScript, as that’s my toolchain of choice, but the server needed for this step can be written in any language and using any framework you’d like.&lt;/p&gt;

&lt;p&gt;As with before, the full source code required to create the server is &lt;a href="https://github.com/tjvantoll/accelerometer-data-ingest"&gt;open source and available on GitHub&lt;/a&gt;, and I’ll be highlighting the most important parts here.&lt;/p&gt;

&lt;p&gt;At the highest level, this endpoint has to listen for POST requests coming in at the root of the server. Here’s how that’s done in Express.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have the request, you next have to parse out the floating-point accelerometer values coming from Notehub. Specifically, the code has to parse the request, get out 32-bit floating point values one at a time, and put them into an array of readings to use later. Here’s what that code looks like in JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;binaryFromRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;binaryFromRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFloat32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFloat32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFloat32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the data aggregated, the last step is to get our data to Edge Impulse. For this, Edge Impulse provides an &lt;a href="https://edgeimpulse.readme.io/reference/ingestion-api"&gt;ingestion API&lt;/a&gt; that expects data in a &lt;a href="https://edgeimpulse.readme.io/reference/data-acquisition-format"&gt;given format&lt;/a&gt;, and places a new entry in your model’s training set.&lt;/p&gt;

&lt;p&gt;Here’s what the code to do that in JavaScript looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emptySignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;protected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;alg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;iat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;emptySignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;device_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;device-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;device_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LIS2HH12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;interval_ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sensors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;units&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m/s2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;units&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m/s2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accZ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;units&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m/s2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://ingestion.edgeimpulse.com/api/training/data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDGE_IMPULSE_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-file-name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error publishing data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have a server you need to make the server available on the public internet. You can do this via your favorite hosting provider, or, if you’re looking for a quick way to test you can use a service like &lt;a href="https://theboroer.github.io/localtunnel-www/"&gt;localtunnel&lt;/a&gt; or &lt;a href="https://ngrok.com/"&gt;ngrok&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m a big fan of ngrok as I’ve found it works well and they have a generous free tier. After &lt;a href="https://ngrok.com/docs/getting-started/"&gt;installing&lt;/a&gt;, you can run ngrok with a simple command from your terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command makes the provided port (in this case 3000) available on the public internet at the provided URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k6nuCBzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yq0t6sfaefvapybkqw52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6nuCBzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yq0t6sfaefvapybkqw52.png" alt="Example usage of ngrok" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have this URL your very last step is to &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/web-transactions"&gt;create a proxy route in Notehub&lt;/a&gt;. The proxy route provides the connection so that Notehub knows to invoke your web server when it receives a &lt;code&gt;web.post&lt;/code&gt; request from the Notecard. Here’s an example of the proxy route you need to create, making sure to provide your own URL for the &lt;strong&gt;URL&lt;/strong&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SzIDWMuy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6s91ecwkodf0bvv4q8bh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SzIDWMuy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6s91ecwkodf0bvv4q8bh.png" alt="An example proxy route in Notehub" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with that—you’re all set. If you start up your firmware on your device it’ll start collecting accelerometer samples. The firmware will then use the Notecard to send your data to Notehub, which invokes your proxy route, which sends the data to Edge Impulse where you can further refine your ML models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y6IND9vK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5yz2fnzjq5uzhajpgb4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y6IND9vK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5yz2fnzjq5uzhajpgb4.gif" alt="A full process of ingesting ML data and posting it to Edge Impulse" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty cool!&lt;/p&gt;

&lt;p&gt;The power of this approach is that it can run in any environment that has cellular connectivity. Meaning, you could adapt this code to run on your production hardware, and start collecting training data from devices in real-world conditions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exactly&lt;/em&gt; how you’ll want to use this code will likely vary. Maybe you’d want to take a few samples a day at different intervals. Maybe you’ll want to create a &lt;a href="https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables"&gt;Notehub environment variable&lt;/a&gt; and only send readings when that variable is activated. The idea behind this sample is to you how the raw data transfer works, and to serve as inspiration for the code and techniques you may want to incorporate into your own projects.&lt;/p&gt;

&lt;p&gt;And speaking of techniques you may want to incorporate, I have one more fun thing to show you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating ML Models
&lt;/h2&gt;

&lt;p&gt;In this article we’re looking at how to implement a robust MLOps process. In the previous section we saw how to gather training data from devices in the field, which allows you to continuously improve your ML models with real data. But that leaves a big problem—after you’ve done the work to build an improved version of your model, how do you get that model back out to your devices in the field?&lt;/p&gt;

&lt;p&gt;The Notecard offers a compelling answer to this question with &lt;a href="https://dev.blues.io/guides-and-tutorials/notecard-guides/notecard-outboard-firmware-update/"&gt;Notecard Outboard Firmware Update&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Notecard Outboard Firmware Update?
&lt;/h3&gt;

&lt;p&gt;Over-the-the firmware updates are table stakes in IoT, but implementing the updates themselves is often a complex and highly risky endeavor. Most firmware-update processes require developers to write code, generally placed within the application itself, or within its RTOS, requiring a special ‘bootloader’ and firmware layout. This requires a high degree of skill, is susceptible to bad updates “bricking” or rendering devices non-functional, and often removes developer choice from the equation.&lt;/p&gt;

&lt;p&gt;Outboard Firmware Update is a Notecard feature that allows device builders to implement OTA firmware updates in their devices without writing any code. Additionally, developers have the freedom of choice, and may select from a large number of microcontrollers (MCUs), programming languages, and real-time operating systems (RTOS), and can even perform updates on ‘native’ applications with no code from Blues and no RTOS at all.&lt;/p&gt;

&lt;p&gt;Outboard Firmware Update is compelling for machine-learning projects like the one we’re discussing in this article, because it allows you update an app’s full ML model, and it allows you to do so remotely, and all without the risk of bricking your devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does Notecard Outboard Firmware Update work?
&lt;/h3&gt;

&lt;p&gt;The Notecard’s docs have a &lt;a href="https://dev.blues.io/guides-and-tutorials/notecard-guides/notecard-outboard-firmware-update/#how-it-works"&gt;convenient checklist&lt;/a&gt; for everything you need to do to perform an outboard firmware update. Let’s look at how each item in that list works in this project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You ensure your hardware is using the required wiring.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because we’re using a Notecarrier F from the Blues Starter Kit for this project, all wiring between the Notecard and our host (the Blues Swan) is already ready to go. If you’re using other boards, see &lt;a href="https://dev.blues.io/guides-and-tutorials/notecard-guides/notecard-outboard-firmware-update/#required-wiring"&gt;this guide&lt;/a&gt; for details on the required wiring.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You enable Notecard Outboard Firmware Update on your Notecard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’re performing the necessary call to make this happen in our Zephyr firmware. Specifically, the sample code performs a &lt;code&gt;card.dfu&lt;/code&gt; request on the Notecard setting &lt;code&gt;"name"&lt;/code&gt; to &lt;code&gt;"stm32"&lt;/code&gt; (because the Swan is an STM32-based MCU), and &lt;code&gt;"on"&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NoteNewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"card.dfu"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"stm32"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddBoolToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;NoteRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You build your firmware image file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This step requires you to build the binary firmware image file you’d like to update your device with. In a production workflow, this step would occur after you have an updated version of your model ready, have placed the updated model into your application, and have done the necessary testing to push the model out to some, or all, of your devices.&lt;/p&gt;

&lt;p&gt;For local testing, I’d recommend adding a simple logging statement near the top of &lt;code&gt;main()&lt;/code&gt; so you can see the firmware update in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[INFO] version: 2.0&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the app updated you’ll need to &lt;a href="https://dev.blues.io/tools-and-sdks/firmware-libraries/zephyr-sdk/#building-and-running"&gt;build your Zephyr app&lt;/a&gt;, and then grab the built binary in your project from &lt;code&gt;build/zephyr/zephyr.bin&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You upload your firmware on Notehub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you have your new binary visit the &lt;strong&gt;Firmware&lt;/strong&gt; section of your Notehub project, click the &lt;strong&gt;Upload firmware&lt;/strong&gt; button, and upload your newly built file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3QYLuXlt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kn603emjp7g83p9vd9ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3QYLuXlt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kn603emjp7g83p9vd9ya.png" alt="The firmware listing in Notehub" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point Notehub has your firmware and is ready to push it out to your devices. To do so, go to your Notehub project’s &lt;strong&gt;Devices&lt;/strong&gt; page, and then select the &lt;strong&gt;Host Firmware&lt;/strong&gt; tab. Here you’ll see all available firmware versions for your host devices, and hitting &lt;strong&gt;Apply&lt;/strong&gt; starts the process of getting the updated firmware on your host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yKH89rO8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s3a5wvdpshvvcrufuvzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yKH89rO8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s3a5wvdpshvvcrufuvzl.png" alt="A look at the page in Notehub that allows you to apply host firmware updates" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Notecard downloads the firmware, verifies it, and performs the update.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you’ve queued up a firmware update in Notehub, the Notecard detects a new host binary is available on its next sync and downloads the firmware into its own flash storage.&lt;/p&gt;

&lt;p&gt;The Notecard will then perform a RESET on the host microcontroller, which places it into its ROM bootloader. Then, using a microcontroller-specific communications protocol, the Notecard reprograms the various areas in flash as directed by instructions within the firmware image file, verifies them via MD5 hashes, and restarts the MCU.&lt;/p&gt;

&lt;p&gt;The great thing is this all happens automatically, and at the end of the process your device will start running your updated model automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vLMG58SE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbcv7ee1808icshgsiay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vLMG58SE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbcv7ee1808icshgsiay.png" alt="A log of the updated version running on a device" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;The big picture is each of the two processes discussed in this article—an easy way to collect ML training data from remote devices, and a workflow to remotely update your models—are both tools that can help you build a more robust MLOps process. The end goal is to avoid shipping your models once and forgetting about them, and to instead establish a process of continuously collecting data, and continuously improving the quality of the models you use for your production apps.&lt;/p&gt;

&lt;p&gt;This article’s samples are reference implementations and are both available on GitHub. You may wish to use their code verbatim, or take the bits and pieces that make sense for your project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tjvantoll/mlops-zephyr/"&gt;Zephyr MLOps Sample&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tjvantoll/accelerometer-data-ingest"&gt;Accelerometer Data Ingest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Blues we create a lot of real-world TinyML samples. If you found this discussion interesting, you might want to check out our &lt;a href="https://dev.blues.io/accelerators/tag/machine-learning"&gt;ML accelerator projects&lt;/a&gt;, and the &lt;a href="https://dev.blues.io/community/projects/machine-learning"&gt;many ML projects our community have built&lt;/a&gt;. And if you have any questions about this post, or want to discuss anything further, &lt;a href="https://discuss.blues.io/"&gt;check out our forum&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>zephyr</category>
    </item>
    <item>
      <title>Should You Care About React Server Components?</title>
      <dc:creator>TJ VanToll</dc:creator>
      <pubDate>Thu, 08 Apr 2021 14:39:40 +0000</pubDate>
      <link>https://forem.com/tjvantoll/should-you-care-about-react-server-components-4dg2</link>
      <guid>https://forem.com/tjvantoll/should-you-care-about-react-server-components-4dg2</guid>
      <description>&lt;p&gt;Developers are busy people, and we don’t always have time to evaluate the JavaScript world’s myriad of frameworks and tools.&lt;/p&gt;

&lt;p&gt;In this article I want to help you decide whether &lt;a href="https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html" rel="noopener noreferrer"&gt;React Server Components&lt;/a&gt; is something that you should check out immediately, or whether you should wait.&lt;/p&gt;

&lt;p&gt;We’ll start by looking at what React Server Components are, then discuss what problems they solve, and wrap up with a conversation on whether you should care or not. Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are React Server Components?
&lt;/h2&gt;

&lt;p&gt;React Server Components are a new experimental feature of React. Here’s how &lt;a href="https://github.com/josephsavona/rfcs/blob/server-components/text/0000-server-components.md" rel="noopener noreferrer"&gt;the React team describes the feature&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Server Components allow developers to build apps that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;client&lt;/em&gt; in the context of Server Components is a web browser. Although React can run in other clients—aka React Native running on iOS and Android—the Server Components feature is currently only concerned with the web.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;server&lt;/em&gt; in the context of Server Components is a JavaScript-based backend like &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea is, Server Components give you the ability to selectively move components from the &lt;em&gt;client&lt;/em&gt;, where the browser executes them, to the server, where something like Express executes them.&lt;/p&gt;

&lt;p&gt;To make it easy to tell the difference, Server Components introduces a new naming convention, where &lt;code&gt;.server.js&lt;/code&gt; files are server components, &lt;code&gt;.client.js&lt;/code&gt; files are client components, and regular &lt;code&gt;.js&lt;/code&gt; files are files that can run in both environments. Here’s what that looks like in the &lt;a href="https://github.com/reactjs/server-components-demo" rel="noopener noreferrer"&gt;React team’s Server Components demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8x3dt6emmaoqtc33baun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8x3dt6emmaoqtc33baun.png" alt="An example of the new naming convention"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, why would I want to do any of this?
&lt;/h2&gt;

&lt;p&gt;Rendering components on a server has a number of potential benefits. The React team’s &lt;a href="https://github.com/josephsavona/rfcs/blob/server-components/text/0000-server-components.md" rel="noopener noreferrer"&gt;full writeup on server components&lt;/a&gt; lists these benefits in great detail, but I’ll summarize what I think are the most important ones here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefit #1: Using third-party dependencies without a file size penalty
&lt;/h3&gt;

&lt;p&gt;One of best-known web performance tips is to minimize the amount of code you ship to your users. As such, front-end developers are hesitant to add large dependencies to their applications, even if those dependencies would save us a lot of time and effort.&lt;/p&gt;

&lt;p&gt;Server Components offer an interesting solution to this problem. Because Server Components can live on a server (and not a client), their dependencies can live on the server as well—allowing you to use dependencies with zero impact on the size of your client-size bundles.&lt;/p&gt;

&lt;p&gt;For example, suppose you’re writing an application that displays user-written Markdown. Two libraries that can help you do that are &lt;a href="https://www.npmjs.com/package/marked" rel="noopener noreferrer"&gt;marked&lt;/a&gt;, which parses Markdown, and &lt;a href="https://www.npmjs.com/package/sanitize-html" rel="noopener noreferrer"&gt;sanitize-html&lt;/a&gt;, which cleans up user-written HTML, including removing potential &lt;a href="https://en.wikipedia.org/wiki/Cross-site_scripting" rel="noopener noreferrer"&gt;XSS&lt;/a&gt; attacks.&lt;/p&gt;

&lt;p&gt;By using those two libraries you can write a simple React component that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* RenderMarkdown.js */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 35.9K (11.2K gzipped)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sanitizeHtml&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sanitize-html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 206K (63.3K gzipped)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RenderMarkdown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanitizedHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitizeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sanitizedHtml&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re writing this component today you have to do a cost-benefit analysis. Are the conveniences of marked and sanitize-html worth the ~75K of gzipped JavaScript being added to your client-side bundle, as well as the performance hit of having your users’ browsers interpret an (un-gzipped) 200K+ of JavaScript code at runtime? Probably?&lt;/p&gt;

&lt;p&gt;Now let’s look at a version of this component that can run on a server as a Server Component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* RenderMarkdown.server.js */&lt;/span&gt;
&lt;span class="c1"&gt;// Same code, but now these dependencies have no client-side penalty&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sanitizeHtml&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sanitize-html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RenderMarkdown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanitizedHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitizeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sanitizedHtml&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only code difference in this version is the file name (&lt;code&gt;RenderMarkdown.server.js&lt;/code&gt; instead of &lt;code&gt;RenderMarkdown.js&lt;/code&gt;), but the behavior difference is fairly substantial. With this version of RenderMarkdown, your user never has to download or interpret marked or sanitize-html, but you still get the benefit of using both to keep Markdown implementation clean.&lt;/p&gt;

&lt;p&gt;This is pretty cool, but before you get too excited, there are some Server Components limitations that will keep you from removing a lot of your client-side dependencies. Here’s the full of things a Server Component can &lt;em&gt;not&lt;/em&gt; do from the &lt;a href="https://github.com/josephsavona/rfcs/blob/server-components/text/0000-server-components.md#sharing-code-between-server-and-client" rel="noopener noreferrer"&gt;React team’s Server Components RFC (Request for Comments)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9lq54thk14gsh358u3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9lq54thk14gsh358u3h.png" alt="List of Server Components limitations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The big ones here are Server Components cannot have state and cannot work with DOM APIs, which means all of your components that use things like &lt;code&gt;useState()&lt;/code&gt; or &lt;code&gt;onChange&lt;/code&gt; are not eligible. This is a big limitation because... most UI components rely on state and DOM APIs—meaning, a lot of your dependencies will have to remain on the client.&lt;/p&gt;

&lt;p&gt;Still, being able to remove some of your code to the server has the potential to lead to noticeable performance gains, especially for larger apps. Facebook stated that &lt;a href="https://www.youtube.com/watch?v=TQQPAU21ZUw&amp;amp;t=2994s" rel="noopener noreferrer"&gt;their first production experiments with Server Components allowed them to remove almost 30% of their code from the client&lt;/a&gt;, which is a big deal.&lt;/p&gt;

&lt;p&gt;And being able to move code to the server is not the only benefit of Server Components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefit #2: Accessing your backend fast
&lt;/h3&gt;

&lt;p&gt;Accessing data is one of the most expensive tasks in modern front-end applications. Because most applications store their data remotely (aka not on the client), getting the data you need involves network calls, and trying to reduce the number of network calls you make, while also keeping your code clean and maintainable, can be a big challenge.&lt;/p&gt;

&lt;p&gt;Server Components have the ability to help here, as you now have the ability to move data-access components to a server, which can access data storage much faster.&lt;/p&gt;

&lt;p&gt;For example, suppose you have a header component that needs to retrieve notifications, a user’s profile, and a user’s subscription. Here’s one way you could write that component today.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Header.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;notifications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setNotifications&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProfile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSubscription&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mycompany.com/notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;setNotifications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mycompany.com/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;setProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mycompany.com/subscription&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;setSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{...}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is not ideal, as your component must wait for three separate network requests to completely render.&lt;/p&gt;

&lt;p&gt;There are ways around this. You could ask a backend developer to build an API just for your header, which would return exactly what you need from multiple locations. But UI-specific APIs aren’t reusable, and therefore difficult to maintain over time. You could also use something like &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; to aggregate your backend API calls, but GraphQL isn’t an option for every company.&lt;/p&gt;

&lt;p&gt;React Server Components offers an interesting new approach to this problem, by allowing you to access your data directly on the server. For example, consider this update to the header that lets you access a database right in your component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Header.server.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-database-of-choice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{...}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Server Components, because you’re running on a server, you have the ability to access server-side resources without making a network round trip. And this ability lets you write cleaner code, as you don’t need to write a bespoke backend API just for the UI, or architect your components to reduce as many network calls as possible.&lt;/p&gt;

&lt;p&gt;That being said, even though the ability to quickly access server-side resources is cool, it’s also not without downsides—the big one being, this is all highly dependent on your backend setup. You stand to gain a lot if your server-side resources are JavaScript-based, but if your server-side resources are in a completely different ecosystem (Java, .NET, PHP, etc), you’ll have a hard time actually gaining much from a Server Component architecture.&lt;/p&gt;

&lt;p&gt;Before we wrap up let’s look at some of the other limitations of Server Components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: I’m only hitting the high-level benefits of server components to keep this discussion brief. If you want to read about all the benefits, I’d recommend reading through &lt;a href="https://github.com/josephsavona/rfcs/blob/server-components/text/0000-server-components.md#motivation" rel="noopener noreferrer"&gt;the section on Server Components benefits from the React team’s RFC&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What are the issues with Server Components?
&lt;/h2&gt;

&lt;p&gt;After spending time with Server Components my biggest complaint is the complexity it introduces to React applications.&lt;/p&gt;

&lt;p&gt;For example, as I started to play around with the &lt;a href="https://github.com/reactjs/server-components-demo" rel="noopener noreferrer"&gt;React team’s Server Components demo&lt;/a&gt;, I realized I had to fundamentally change how I approached building components. Instead of just creating a new file and typing &lt;code&gt;export const MyComponent = () =&amp;gt; {}&lt;/code&gt;, I now had to start thinking about how the component would be used, to help determine whether it was a better fit for the client or the server.&lt;/p&gt;

&lt;p&gt;And that’s just when creating the components. As Server Components advance, some of those same concerns are going to apply to how you unit test your Server Components, and also how to debug these components when things go wrong.&lt;/p&gt;

&lt;p&gt;For example, currently React Server Components return “a description of the rendered UI, not HTML”, which I’m sure is important to the implementation, but it does mean that the response you see in your developer tools looks like nonsense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuwj49bj0t5oktd4aqvq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuwj49bj0t5oktd4aqvq.png" alt="The output of a Server Component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To be fair, most of these limitations come from Server Components being so new. The React team has stated that they expect most of the initial adoption to be through frameworks like &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; early on, so it would make sense that some of these workflows are a bit rough today.&lt;/p&gt;

&lt;h2&gt;
  
  
  So should you care?
&lt;/h2&gt;

&lt;p&gt;In my opinion there are a three groups of people that should care about Server Components today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) If you are a developer on a framework like Next.js.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Frameworks like Next.js are a logical consumer of React Server Components, as Next.js is already a framework that users server-side code to help React apps run faster.&lt;/p&gt;

&lt;p&gt;These frameworks also have the ability to help hide some of the messy details of the underlying React implementation, making Server Components easier for your average developer to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) If your company is operating at Facebook’s scale.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In its current state, React Server Components introduces a lot of complexity for small performance gains.&lt;/p&gt;

&lt;p&gt;For companies like Facebook this sort of tradeoff makes sense, as they have the engineering capacity to deal with this complexity, and marginal performance gains are a big deal for web applications operating at Facebook’s scale.&lt;/p&gt;

&lt;p&gt;That being said, most companies don’t operate at Facebook’s scale, and therefore most companies have no need to evaluate Server Components in its current state. You can wait until the feature stabilizes, or appears in a framework like Next.js&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) If you like tinkering with the latest and greatest.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reason I looked into Server Components is I think they’re a cool idea with a lot of potential. The line between the client and server is getting blurry in the front-end world, and I think we’re going to see more experiments that try to mix and match the two environments to help developers build the best possible web applications.&lt;/p&gt;

&lt;p&gt;With that in mind, if you’re the type of person that likes to experiment with the latest and greatest, React Server Components is well worth trying. The &lt;a href="https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html" rel="noopener noreferrer"&gt;Server Components intro video&lt;/a&gt; is excellent, &lt;a href="https://github.com/josephsavona/rfcs/blob/server-components/text/0000-server-components.md" rel="noopener noreferrer"&gt;the React team’s RFC&lt;/a&gt; is a well-written guide that details how everything works. There’s also an &lt;a href="https://github.com/reactjs/rfcs/pull/188" rel="noopener noreferrer"&gt;open pull request where you can submit your own feedback&lt;/a&gt; on the feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Overall, Server Components is still too early for your average developer to care about, but it’s a fascinating idea with a lot of potential for those that want to guide the future of React and web development.&lt;/p&gt;



&lt;h2&gt;
  
  
  Master the Art of React UI with KendoReact
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.telerik.com/kendo-react-ui" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuysownz801fx230mkh6.png" alt="Learn more about KendoReact"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.telerik.com/kendo-react-ui/" rel="noopener noreferrer"&gt;KendoReact&lt;/a&gt; is a professional UI components and data visualization library for React on a mission to help you design and build business apps with React much faster. With KendoReact, developers get an immediate productivity boost and businesses get shorter time-to-market. Designed and built from the ground up for React, KendoReact plays well with any existing UI stack. Its 90+ customizable and feature-rich components make it the perfect foundation for your internal UI library.&lt;/p&gt;

&lt;p&gt;Built by a team with 19+ years of experience in making enterprise-ready components, this library is lightning fast, highly customizable and fully accessible, delivering support for WCAG 2.1, Section 508, and WAI-ARIA a11y standards. You can find detailed accessibility compliance information &lt;a href="https://www.telerik.com/kendo-react-ui/components/accessibility/accessibility-compliance/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>My Top 5 Tech Speaking Tips</title>
      <dc:creator>TJ VanToll</dc:creator>
      <pubDate>Wed, 21 Aug 2019 18:44:41 +0000</pubDate>
      <link>https://forem.com/progress/my-top-5-tech-speaking-tips-4dej</link>
      <guid>https://forem.com/progress/my-top-5-tech-speaking-tips-4dej</guid>
      <description>&lt;p&gt;This article is an eclectic set of speaking tips I’ve learned from giving &lt;a href="https://www.tjvantoll.com/speaking/" rel="noopener noreferrer"&gt;dozens of tech talks at conferences around the world&lt;/a&gt;. I hope these tips help new speakers prepare their first talk, and help give experienced speakers ideas to potentially improve.&lt;/p&gt;

&lt;p&gt;One thing I’ll note though: these are tips that I’ve found work for &lt;em&gt;me&lt;/em&gt;, which doesn’t necessarily mean they’ll work for you. Each speaker needs to find their own style and workflow, so try to take inspiration from these tips and don’t interpret them as hard and fast rules.&lt;/p&gt;

&lt;p&gt;With that disclaimer out of the way, on to the tips!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tip #1: Don’t worry about slide design&lt;/li&gt;
&lt;li&gt;Tip #2: Make an outline&lt;/li&gt;
&lt;li&gt;Tip #3: During slide creation, make your intro slides last&lt;/li&gt;
&lt;li&gt;Tip #4: Make the audience do stuff&lt;/li&gt;
&lt;li&gt;Tip #5: Speak—a lot&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="tip-1"&gt;Tip #1: Don’t worry about slide design&lt;/h2&gt;

&lt;p&gt;I’ve never cared about slide design and you probably shouldn’t either.&lt;/p&gt;

&lt;p&gt;I offer this as a tip because I find that new speakers spend a lot of time trying to find a good color scheme or font for their slides. Or get confused about whether to use PowerPoint, or Keynote, or something web-based.&lt;/p&gt;

&lt;p&gt;You don’t want this sort of thing to get in your way. Start with a tool you like (for me that’s PowerPoint because it’s simple), pick the blank slide template, and get started.&lt;/p&gt;

&lt;p&gt;Just to give you an idea, I typically have three types of slides that I use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design #1: Plain text
&lt;/h3&gt;

&lt;p&gt;I use simple slides with just text on them a whole lot. For colors I use black text on a white background for readability, and I use the Avenir font—but only because that was the font on some slide deck I started with years ago. It looks nice to me though.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdk60s6za5mldtv302qdl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdk60s6za5mldtv302qdl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Design #2: Screenshots
&lt;/h3&gt;

&lt;p&gt;For a lot of my slides I just take a screenshot of something and then just toss it on a slide. It works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F92v5vytlkcazp15xzpkj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F92v5vytlkcazp15xzpkj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Design #3: Bullets
&lt;/h3&gt;

&lt;p&gt;I use bulleted slides when I need to present a list, and I just use the default PowerPoint design for this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq2e5zhavw3g0ynaq0rig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq2e5zhavw3g0ynaq0rig.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do occasionally mix things up, but that’s the approach I take on 95% of the slides I make. And for most cases that’s all you’ll need as well.&lt;/p&gt;

&lt;p&gt;Unless you’re an actual designer and want to spend the time to create something really unique, I’d encourage you just to pick the blank slide deck in your tool of choice and call it good enough.&lt;/p&gt;

&lt;p&gt;My second tip deals with what to do next.&lt;/p&gt;

&lt;h2 id="tip-2"&gt;Tip #2: Make an outline&lt;/h2&gt;

&lt;p&gt;I make an outline for every talk I give. The outline is dead simple, and includes only three things: the talk’s main audience takeaway, 3–4 topics that help support that takeaway, and an actionable next step.&lt;/p&gt;

&lt;p&gt;When written out that structure looks something like this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State the takeaway, aka what I want the audience to get out of the talk.&lt;/li&gt;
&lt;li&gt;Topic #1&lt;/li&gt;
&lt;li&gt;Topic #2&lt;/li&gt;
&lt;li&gt;Topic #3&lt;/li&gt;
&lt;li&gt;Topic #4 if necessary&lt;/li&gt;
&lt;li&gt;Restate the takeaway again&lt;/li&gt;
&lt;li&gt;Give an actionable next step&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, below is the outline I had for a &lt;a href="https://www.tjvantoll.com/speaking/slides/AR/Montreal/ar.pdf" rel="noopener noreferrer"&gt;&lt;em&gt;Is Augmented Reality the Future?&lt;/em&gt; talk&lt;/a&gt; I recently gave in Montreal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takeaway: AR (Augmented Reality) is cool, but still pretty hard for your average developer.&lt;/li&gt;
&lt;li&gt;Topic #1: History of AR&lt;/li&gt;
&lt;li&gt;Topic #2: AR today&lt;/li&gt;
&lt;li&gt;Topic #3: Building AR apps&lt;/li&gt;
&lt;li&gt;Restate the takeaway&lt;/li&gt;
&lt;li&gt;Provide my preferred place to get started building AR apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I have this outline I start including the outline in my slide deck directly. Specifically, I create a slide called “Agenda” that looks something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fb9381f381avohhfjnp94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fb9381f381avohhfjnp94.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This act of creating an outline helps you organize the talk in your head, and placing that outline in slides helps you visually see your progress creating content for each topic. Audiences like structure as well, as it helps them know what to expect from your talk, and therefore makes it easier for them to follow along.&lt;/p&gt;

&lt;p&gt;To make it even easier for the audience, I always include the same agenda slide in my deck when I transition between topics. For example, after I complete the “Brief history of AR” topic I show the slide below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc671o4izaykpwhio1s6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc671o4izaykpwhio1s6f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These slides give me a logical place to summarize the previous topic and segue to the next.&lt;/p&gt;

&lt;p&gt;Next, I create a slide that summarizes the key takeaways of my talk at the beginning and end of my talk. Here’s what that slide looked like for my AR talk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FsL6BmLt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FsL6BmLt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I show this slide at the beginning of my talk I say something like, &lt;em&gt;“This is what I plan to prove/show you today”&lt;/em&gt;, and at the end I restate the points, and usually talk about how the topics I presented support those points.&lt;/p&gt;

&lt;p&gt;Once you have this basic outline in place you’re ready to start developing your introduction and your main content.&lt;/p&gt;

&lt;h2 id="tip-3"&gt;Tip #3: During slide creation, make your intro slides last&lt;/h2&gt;

&lt;p&gt;When creating slides most people start with the introduction, because, well, it’s the first thing you actually present. The problem is that creating compelling introductions can be hard, even for experienced speakers.&lt;/p&gt;

&lt;p&gt;And because of this, it’s really easy to waste a lot of time searching for the perfect way to introduce your topic, when in reality it’s fairly insignificant.&lt;/p&gt;

&lt;p&gt;Your introduction can be as simple as stating the following points, especially when you’re just getting started.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’m &lt;em&gt;&amp;lt;your name&amp;gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;State topic&lt;/li&gt;
&lt;li&gt;State audience takewaways&lt;/li&gt;
&lt;li&gt;Show agenda&lt;/li&gt;
&lt;li&gt;Start talking about topic 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By saving the introduction until the end you’ll have a better idea of what you might want to say to start your talk with. You can also rest assured that the bulk of your work is done.&lt;/p&gt;

&lt;p&gt;All that being said, I don’t mean to imply introductions aren’t worth spending &lt;em&gt;any&lt;/em&gt; time on, because done well they can be way to engage the audience and draw them into your talk. I would just save them for last.&lt;/p&gt;

&lt;p&gt;When you do create an introduction you need to experiment a bit and find what works for you. My go-to introduction is to telling a story that relates to my topic or the place I’m speaking. For example, to open my AR talk I told a story about my love for Pokémon GO, and how that’s what got me interested in AR in the first place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzy4jmnpk04fp07x5vh3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzy4jmnpk04fp07x5vh3y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was lucky enough to give a talk in Denmark last year, and I opened with a slide with pictures of me around the area for fun.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fial3omlpspux05035974.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fial3omlpspux05035974.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have an idea for a story to tell I’d encourage you to take it and run with it. Deliver the story with confidence and have fun.&lt;/p&gt;

&lt;p&gt;But if you don’t have an idea don’t worry either. Save the intro for the end, and if you can’t think of anything, stick with the basics and get into the meat of your talk right away.&lt;/p&gt;

&lt;p&gt;And when you’re delivering the bulk of your content I have a tip for that as well.&lt;/p&gt;

&lt;h2 id="tip-4"&gt;Tip #4: Make the audience do stuff&lt;/h2&gt;

&lt;p&gt;Walk into a room halfway through a random tech talk and you’ll likely see at least half the attendees looking at their phones or laptops.&lt;/p&gt;

&lt;p&gt;The thing is, it’s hard to keep people’s attention on anything, and tech topics aren’t the world’s most engaging material. As a speaker there are a few things you can do to help keep your audience’s attention.&lt;/p&gt;

&lt;p&gt;The simplest thing you can do is poll your audience at given points in your talks. I often do this before I introduce a new topic. For example, in a talk I recently gave called &lt;em&gt;One Project. One Language. Three Apps.&lt;/em&gt; I used the slide below to introduce two frameworks, NativeScript and React Native.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhg3q2rdvcpg6mvmtw6ck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhg3q2rdvcpg6mvmtw6ck.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before I introduced each framework I asked the audience, &lt;em&gt;“How many people here are familiar with React Native”&lt;/em&gt;, and then, &lt;em&gt;“How many people here are familiar with NativeScript”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;These simple questions accomplish two things. First, it forces the audience to look up and engage with the talk, if only for a minute. Second, it helps me get a read on the room. If I see 5% of hands go up I know I need to give a thorough introduction, whereas if every hand goes up, I know a short explanation will suffice.&lt;/p&gt;

&lt;p&gt;Polling the audience is the easiest way to engage the audience, but there are a variety of more creative ways you can accomplish the same goal.&lt;/p&gt;

&lt;p&gt;For example my significant other gives talks on CSS, and one fun thing she does is include mini quizzes in her slides, like the one below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frijzhhmzwp2suid75854.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frijzhhmzwp2suid75854.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This again solves multiple goals. First, it forces the audience to not only pay attention, but to also really try to understand the content to get the correct answer. (It’s 600px, by the way.)&lt;/p&gt;

&lt;p&gt;Second, it gives the presenter a good sense of whether the audience is following along with the content. If you see a lot of hands going up and a lot of correct answers you know people are understanding your material.&lt;/p&gt;

&lt;p&gt;There are professional tools out there to make this sort of audience quizzing even more engaging. For example the service &lt;a href="https://kahoot.com/" rel="noopener noreferrer"&gt;Kahoot!&lt;/a&gt; makes it easy to configure quizzes that an audience can easily complete on their laptops and smartphones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fregd84ouopifobzmjzum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fregd84ouopifobzmjzum.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall, the specific tool or approach doesn’t really matter. What does matter is finding some way to break up the inherent monotony of a tech talk by engaging the audience.&lt;/p&gt;

&lt;h2 id="tip-5"&gt;Tip #5: Speak—a lot&lt;/h2&gt;

&lt;p&gt;The phrase &lt;em&gt;practice makes perfect&lt;/em&gt; applies more to public speaking than any other life skill. No amount of practicing in front of a mirror will prepare you for the experience of delivering a talk in front of a live audience, and the only best way to improve is continue to do just that—deliver talks, repeatedly.&lt;/p&gt;

&lt;p&gt;This doesn’t mean you have to constantly give talks at conferences around the world though. Occasionally giving a presentation at a local user group, or for a few coworkers at work, will help you learn how to be in front of an audience and deliver your material effectively.&lt;/p&gt;

&lt;p&gt;As you get more comfortable speaking you’ll want to work on how to improve the quality of your talks. And for that I have two recommendations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watch yourself on video
&lt;/h3&gt;

&lt;p&gt;Most people would choose elective surgery over watching a video of themselves presenting—myself included. But it’s extremely important, as you’ll learn things about your talks you can only see from a third-person perspective.&lt;/p&gt;

&lt;p&gt;Personally I started as a heavy &lt;em&gt;“um”&lt;/em&gt; user, which I had no idea about until I saw myself on video. Later, I developed a habit of using my hands... a lot. It was awkward. But knowing these tendencies helps me consciously think about avoiding these habits, and ultimately helps me stop repeating them in the future.&lt;/p&gt;

&lt;p&gt;It’s important. Get a friend to record you and watch the video. You’ll hate it, but it’s worth it. Promise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find a trusted friend for feedback
&lt;/h3&gt;

&lt;p&gt;In addition to watching yourself speak, it’s good to get feedback from others as well. However here you have to be extremely careful for two reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reason #1&lt;/strong&gt;: &lt;strong&gt;Some people are horrible&lt;/strong&gt;, and will leave negative feedback on your talk for silly reasons, like not liking the framework you’re using (seriously).&lt;/p&gt;

&lt;p&gt;And even if you know the feedback is unreasonable, the human brain has an unfortunate tendency to fixate on the negative, even if you get an onslaught of positive feedback to go with it.&lt;/p&gt;

&lt;p&gt;And I can say this from many personal experiences. Remember the talk I mentioned earlier that I gave in Denmark? It’s the most well received talk I’ve ever given. I’ve had random people at conferences compliment me on it; it’s shown up in all sorts of positive tweets, and has gotten numerous mentions in popular newsletters and such.&lt;/p&gt;

&lt;p&gt;This was great, until I made the classic mistake of &lt;a href="https://www.youtube.com/watch?v=AQOEZVG2WY0" rel="noopener noreferrer"&gt;reading the negative comments on the YouTube recording&lt;/a&gt;, which if I’m honest, has ruined a lot of the positive experience I had from preparing and delivering that talk.&lt;/p&gt;

&lt;p&gt;Overall, be careful asking for feedback broadly because the human brain has the not-at-all-useful tendency to heavily focus on the negative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reason #2&lt;/strong&gt;: &lt;strong&gt;Most people won’t be honest&lt;/strong&gt;. If you deliver a talk to a group of friends or coworkers and ask for feedback, the vast majority of people will reply with something like &lt;em&gt;“It was good.”&lt;/em&gt; or &lt;em&gt;“I liked it.”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Although this is supportive, it’s also good to get honest feedback on how you can improve. For this you need to find that blunt person in your life that’s not afraid to tell you like it is, and will call you out on things you can improve on.&lt;/p&gt;

&lt;p&gt;The big difference between asking a friend and random people is a friend can provide context and details on things you need to work on. There’s a big difference between &lt;em&gt;“That talk sucked”&lt;/em&gt; and &lt;em&gt;“I had trouble following along in the middle part of the talk. I think you could have explained the concepts better or maybe used more examples to prove your points.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can also ask questions of your friend, like &lt;em&gt;“did you understand the first topic?”&lt;/em&gt;, or &lt;em&gt;“did I use my hands too much?”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you’re having trouble finding that blunt person in your life try being blunt yourself. I find that people that give generic &lt;em&gt;“it was good”&lt;/em&gt; feedback find much more to say if you press them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final tip: Just do it!
&lt;/h2&gt;

&lt;p&gt;Ok that’s it for now. I have a lot to say because speaking has been very important to me over the last 6–7 years of my life, and it’s opened up a ton of opportunities that I wouldn’t have had otherwise.&lt;/p&gt;

&lt;p&gt;Hopefully you found these tips helpful. If so let me know, because I could easily come up with 5 more 😉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: If you want to see me speak in person I’ll be at &lt;a href="https://www.telerik.com/devreach" rel="noopener noreferrer"&gt;DevReach in Sofia, Bulgaria in October&lt;/a&gt;, and at at &lt;a href="https://jsmobileconf.com/" rel="noopener noreferrer"&gt;jsMobileConf in Boston in November&lt;/a&gt;. Both are great events.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>speaking</category>
    </item>
    <item>
      <title>Why ‘Sign in with Apple’ is Actually Pretty Great</title>
      <dc:creator>TJ VanToll</dc:creator>
      <pubDate>Tue, 02 Jul 2019 16:29:41 +0000</pubDate>
      <link>https://forem.com/progress/why-sign-in-with-apple-is-actually-pretty-great-31fl</link>
      <guid>https://forem.com/progress/why-sign-in-with-apple-is-actually-pretty-great-31fl</guid>
      <description>&lt;p&gt;I believe that Apple’s &lt;a href="https://www.zdnet.com/article/wwdc-2019-apple-announces-sign-in-with-apple-feature/" rel="noopener noreferrer"&gt;recently announced ‘Sign in with Apple’ workflow&lt;/a&gt; is an important step for improving the security and usability of login screens.&lt;/p&gt;

&lt;p&gt;In this article I’ll explain what ‘Sign in with Apple’ is, why I believe it’s a great thing for users, the drama around Apple’s implementation, and some of the lingering concerns I have.&lt;/p&gt;

&lt;p&gt;The crux of my argument is that Apple’s new workflow provides badly needed innovation for a process everyone hates—logging in. Even if Apple’s implementation is flawed (which we’ll get to), hopefully this effort will spur other ideas from other tech giants in response, ideally making the login process better for everyone. To show what I’m talking about let’s start with a bit of background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why passwords are the worst
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://blog.lastpass.com/2018/05/psychology-of-passwords-neglect-is-helping-hackers-win.html/" rel="noopener noreferrer"&gt;2018 survey done by LastPass&lt;/a&gt; showed that 59% of people always or mostly use the same password—everywhere. The same study found that 45% of people don’t bother changing their password when their accounts get hacked, and that 42% of people keep passwords in a file, like, a Word doc or Excel spreadsheet.&lt;/p&gt;

&lt;p&gt;As I write this article, the top-selling book in &lt;a href="https://www.amazon.com/gp/bestsellers/books/3705/ref=zg_b_bs_3705_1" rel="noopener noreferrer"&gt;Amazon’s “Internet &amp;amp; Telecommunications” category&lt;/a&gt; is the &lt;em&gt;internet address &amp;amp; password logbook&lt;/em&gt;. Sixth place in the same category is the &lt;em&gt;INTERNET PASSWORD LOGBOOK&lt;/em&gt;, which honestly has a nicer design than the top seller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwq6lohfpa6q8h929at6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwq6lohfpa6q8h929at6b.png" alt="A list of best selling internet &amp;amp; telecommunications books on Amazon"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;We have failed. Failed &lt;strong&gt;hard&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As easy as it is to mock books like this, it’s important to remember that these books are solving a real problem. Using unique passwords is important, but remembering multiple passwords in your head is impossible, and not everyone has the money and tech savviness to use a password manager.&lt;/p&gt;

&lt;p&gt;Funnily enough, you can make a compelling argument that physical-password-book users are more secure than password reusers, assuming the physical-password-book users create unique passwords for each service they use.&lt;/p&gt;

&lt;p&gt;Regardless, the sheer existence of these books is indicative of just how broken the login process is for most users today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making login better
&lt;/h2&gt;

&lt;p&gt;Everyone knows the current way we log in is not ideal, and plenty of tech companies have tried to make this process better. One big improvement to login screen usability was the advent of Single Sign-On, wherein the user uses a single account to authenticate with multiple apps or services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0d89ef7sl0wstdwmuad1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0d89ef7sl0wstdwmuad1.png" alt="A device showing sign in with Google and sign in with Facebook buttons"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An example of Single Sign-On in an iOS app. The user can use Google or Facebook to authenticate.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The main appeal of SSO is that users don’t have to create and remember a new set of credentials, allowing them to maintain a one-password lifestyle, without the normal downsides of password reuse.&lt;/p&gt;

&lt;p&gt;However, despite its appeal, SSO has its drawbacks as well. Specifically, most SSO providers require that you provide your email address (and sometimes other information as well) to the service you’re authenticating with.&lt;/p&gt;

&lt;p&gt;While providing an email address seems innocuous, shady services will often sell lists of email addresses to advertisers or spammers, greatly increasing the spam mail you have to deal with. Far worse, even if a service has no malicious intentions, data breaches can expose your email to hackers worldwide, opening up the possibility of attacks on accounts you’ve created around the Web with that same email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Apple
&lt;/h2&gt;

&lt;p&gt;With this backdrop in mind, earlier this month Apple announced a new ‘Sign in with Apple’ feature that, at first glance, works very similarly to the SSO mechanisms from Google, Facebook, and others that thousands of apps use today.&lt;/p&gt;

&lt;p&gt;However, Apple’s new authentication workflow offers one innovative twist: when you sign in with Apple, you get to choose whether you want to share your email with the service you’re signing up for, or, whether to hide it from that service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Feeq7lt81ljcx55ak2q7a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Feeq7lt81ljcx55ak2q7a.jpg" alt="An example of ‘Sign in with Apple’ in action in a sample app"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An example of ‘Sign in with Apple’ in action in a sample app.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you choose to hide your email, Apple will create a randomly generated Apple-hosted email address, which will receive all messages on your behalf, and forward them to your legitimate email address.&lt;/p&gt;

&lt;p&gt;This workflow alleviates many of the privacy concerns associated with similar SSO solutions from companies like Google and Facebook. If a shady service sells your email to an advertiser, you’ll be able to easily turn off email forwarding to stop the unwanted spam. Even better, if your generated email address gets exposed in a data breach, you can rest assured, as your Apple-generated email address is unique, therefore hackers will be unable to use that address in subsequent attacks.&lt;/p&gt;

&lt;p&gt;It’s an elegant solution to a common problem; a solution that the likes of Google and Facebook might be hesitant to replicate, because each are companies that earn the bulk of their revenue from advertisers—advertisers that very much like to track users by their email address around the Web. Because Apple does not rely on a similar revenue model, they’re uniquely positioned to innovate and offer users a privacy-focused authentication option. That being said, Apple is not without its own motives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The drama
&lt;/h2&gt;

&lt;p&gt;Apple has a vested interest in being seen as a privacy-focused company, therefore, for them, creating and promoting the ‘Sign in with Apple’ workflow makes a lot of sense.&lt;/p&gt;

&lt;p&gt;More interesting is the news that broke shortly after WWDC, when developers found documentation that stated Apple will &lt;strong&gt;require&lt;/strong&gt; apps that support third-party logins to provide a way to sign in with Apple. Here’s the &lt;a href="https://developer.apple.com/news/?id=06032019j" rel="noopener noreferrer"&gt;exact wording from Apple’s site&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Sign In with Apple will be available for beta testing this summer. It will be required as an option for users in apps that support third-party sign-in when it is commercially available later this year.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This controversial decision immediately sparked a heated debate in the tech world. Those against Apple’s decision argued that Apple is using their monopolistic control over the iOS platform to advance their own SSO solution.&lt;/p&gt;

&lt;blockquote data-lang="en"&gt;
&lt;p&gt;Leveraging their position as gated community guard to push their own single sign on product?  Yeah, thats going to get looked at by the EU at the very least.  Doesn't matter if its an additional option or not, the fact that its required is enough...&lt;/p&gt;— Richard Price (&lt;a class="mentioned-user" href="https://dev.to/richardprice"&gt;@richardprice&lt;/a&gt;) &lt;a href="https://twitter.com/RichardPrice/status/1135680325244182528?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 3, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;blockquote data-lang="en"&gt;
&lt;p&gt;woah. It is "walled garden" moves like this that make me glad I write web apps.&lt;/p&gt;— Jared Beck (@jaredowenbeck) &lt;a href="https://twitter.com/jaredowenbeck/status/1135674803850022912?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 3, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;blockquote data-lang="en"&gt;
&lt;p&gt;Monopolist gonna monopolist&lt;/p&gt;— Caitlin Fitzharris (@caitlintackles) &lt;a href="https://twitter.com/caitlintackles/status/1136101790112395265?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 5, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;The timing is indeed interesting, as &lt;a href="https://www.progress.com/blogs/is-the-ios-app-store-a-monopoly-and-is-that-a-good-thing" rel="noopener noreferrer"&gt;Apple is actively being sued by developers and consumers for its App Store policies&lt;/a&gt;. You might think the company would avoid potentially monopolistic moves while other parts of iOS have active anti-trust lawsuits pending—but apparently Apple isn’t worried, or they don’t believe their actions here are monopolistic.&lt;/p&gt;

&lt;p&gt;On the other side of the argument, those in favor of Apple’s decision to require ‘Sign in with Apple’ argue that Apple &lt;em&gt;has&lt;/em&gt; to require their new workflow for apps to actually use the feature.&lt;/p&gt;

&lt;blockquote data-lang="en"&gt;
&lt;p&gt;The problem "Sign In With Apple" is that it's so delightfully privacy-safe that developers won't offer it. They crave your real email address for reengagement campaigns. Fb happily provides it.&lt;/p&gt;— Josh Constine (@JoshConstine) &lt;a href="https://twitter.com/JoshConstine/status/1135648571196239872?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 3, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;blockquote data-lang="en"&gt;
&lt;p&gt;Seems like it'd have to be. Otherwise it'd share the same fate as all that BrowserID, OpenID kinda stuff. I think apps often support SSO because they want the data that comes with it. Most were never interested in supporting an option that didn't come with the data.&lt;/p&gt;— Moxie Marlinspike (&lt;a class="mentioned-user" href="https://dev.to/moxie"&gt;@moxie&lt;/a&gt;) &lt;a href="https://twitter.com/moxie/status/1135769100305592326?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 4, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;This side of the argument is compelling. For better or worse, there’s very little incentive for individual apps to implement privacy-focused features for users, so it stands to reason that most would just ignore Apple’s new API if they could.&lt;/p&gt;

&lt;p&gt;Remember BrowserID? It was a &lt;a href="https://hacks.mozilla.org/2011/07/introducing-browserid-easier-and-safer-authentication-on-the-web/" rel="noopener noreferrer"&gt;feature championed by Mozilla to provide SSO across the web using your email address&lt;/a&gt;. It was a great idea, and had some real benefits for users, but it never took off because &lt;a href="https://techcrunch.com/2014/03/08/mozilla-stops-developing-its-persona-sign-in-system-because-of-low-adoption/" rel="noopener noreferrer"&gt;very few developers actually integrated it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re an app maker, why would you implement BrowserID or ‘Sign in with Apple’ when you have a roadmap full of backlogged tasks? Moreover, the Apple-based workflow aims to prevent your service from gathering user data—data that all marketing and sales departments will have a hard time giving up unless forced.&lt;/p&gt;

&lt;p&gt;So although ‘Sign in with Apple’ isn’t perfect, and although it’s concerning to give Apple even more control over iOS users, at least the mandate will force all apps that use third-party logins to seriously reassess how they handle their user’s data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The broader point
&lt;/h2&gt;

&lt;p&gt;More broadly speaking, my hope is that Apple’s push here will lead to further advancements into the password management world. It’s 2019 and most people are buying password managers, buying books to write down their passwords, or just giving up and logging in with their cat’s name across the Web. I think we have every right to demand more of the tech giants to help fix this.&lt;/p&gt;

&lt;p&gt;For example, both Google and Facebook claim to care about their users’ privacy, and both have the resources to create similar systems, so why shouldn’t we demand they provide a way to authenticate into third-party apps with similar privacy protections? Better yet, what if a tech company reimagined a BrowserID-like solution that avoids one company having total control?&lt;/p&gt;

&lt;p&gt;There’s no easy win here, but hopefully Apple’s assertive move will spark innovations that move user security and privacy forward.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://www.progress.com/" rel="noopener noreferrer"&gt;Progress&lt;/a&gt; we already have you covered to work with Apple’s new APIs. &lt;a href="https://www.nativescript.org/" rel="noopener noreferrer"&gt;NativeScript&lt;/a&gt; users can start experimenting with ‘Sign in with Apple’ today with the new &lt;a href="https://github.com/EddyVerbruggen/nativescript-apple-sign-in" rel="noopener noreferrer"&gt;nativescript-apple-sign-in plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And because ‘Sign in with Apple’ is based on OAuth 2.0, &lt;a href="https://www.progress.com/kinvey" rel="noopener noreferrer"&gt;Kinvey&lt;/a&gt; users will be able to work with Apple’s new workflow with &lt;a href="https://devcenter.kinvey.com/nativescript/guides/mobile-identity-connect" rel="noopener noreferrer"&gt;Kinvey’s Mobile Identity Connect&lt;/a&gt; later this fall.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>mobile</category>
      <category>security</category>
    </item>
    <item>
      <title>What the Apple Supreme Court Decision Means for Developers</title>
      <dc:creator>TJ VanToll</dc:creator>
      <pubDate>Thu, 16 May 2019 13:14:08 +0000</pubDate>
      <link>https://forem.com/progress/what-the-apple-supreme-court-decision-means-for-developers-22bh</link>
      <guid>https://forem.com/progress/what-the-apple-supreme-court-decision-means-for-developers-22bh</guid>
      <description>&lt;p&gt;Earlier this week &lt;a href="https://www.theverge.com/2019/5/13/18617727/apple-v-pepper-antitrust-illinois-brick-supreme-court-case-loss"&gt;the US Supreme Court allowed an antitrust lawsuit against Apple to proceed&lt;/a&gt;. If you’re like me this decision sounded super important... but also super confusing.&lt;/p&gt;

&lt;p&gt;To try to clear things up, in this article, we’ll break down what this ruling means for your average developer, the ruling’s effect on the iOS ecosystem, and the ruling’s implications on the future of mobile development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the ruling mean?
&lt;/h2&gt;

&lt;p&gt;Let’s start with a little background. If you develop an app for the iOS platform, the only way to get that app to users is through the iOS App Store, and Apple takes a 30% cut of all transactions that occur there. That cut ends up being incredibly lucrative for Apple, as App Store revenues were &lt;a href="https://www.cultofmac.com/601492/app-store-google-play-revenue-2018/"&gt;a staggering 46.6 &lt;em&gt;billion&lt;/em&gt; dollars in 2018&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This court case calls Apple’s app-distribution model into question. Specifically, the case’s plaintiff alleged that, by requiring iOS users to buy apps exclusively from the App Store, and taking a 30% commission on all transactions that occur there, Apple is adding a fee that developers pass on to customers. Simply put, the plaintiffs claimed that Apple is using their monopoly over app distribution to raise prices for iOS customers.&lt;/p&gt;

&lt;p&gt;Apple’s counterargument was that iOS users buy apps from developers and not Apple, and therefore Apple is not subject to some complicated provisions of US antitrust laws. (&lt;a href="https://en.wikipedia.org/wiki/Apple_Inc._v._Pepper"&gt;The specifics involve a 1970s legal doctrine called &lt;em&gt;Illinois Brick&lt;/em&gt; and a kind-of confusing “indirect purchaser” discussion.&lt;/a&gt; Being a lawyer must be so fun.)&lt;/p&gt;

&lt;p&gt;The court ruled against Apple, and in a 5–4 decision, allowed iOS customers to sue Apple for allegedly driving up prices of iOS apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What effect does this ruling have?
&lt;/h2&gt;

&lt;p&gt;Let’s start with one important point: this ruling only &lt;em&gt;allows&lt;/em&gt; iOS customers to sue Apple for their app-distribution business model; it did not rule that the App Store violates anti-trust law. However, this ruling clears the way for legal proceedings that attack the App Store’s business model directly, which could force Apple to fundamentally change the way app distribution on iOS works.&lt;/p&gt;

&lt;p&gt;That being said, it’s possible (likely even) that the subsequent cases that challenge this model will take years to play out. After all, &lt;a href="https://www.courtlistener.com/recap/gov.uscourts.cand.249697.124.0.pdf"&gt;the initial court procedures that started the just-resolved case were made in December of 2013&lt;/a&gt;. Therefore, don’t expect this ruling to have any short-term effect on way you build and distribute iOS applications.&lt;/p&gt;

&lt;p&gt;One reason the proceedings may take longer than expecting is dealing with the finer points of the law. For example, suppose the court rules that Apple’s model indeed violated anti-trust policies, and consequently raised prices for consumers. How do you determine the amount prices were raised? And who does the extra money go to? Customers? Developers? The current suit was done on behalf of customers, but it’s very possible that a class-action lawsuit on the behalf of developers could complicate the issue.&lt;/p&gt;

&lt;p&gt;Long term, if legal restrictions do come down on Apple they’re likely positive effect on iOS developers. We might see a future where Apple is forced to permit third-party app stores, or to provide ways for users to side load apps—each of which would give developers more control in how they get their apps to their users.&lt;/p&gt;

&lt;p&gt;Interestingly, these changes would make iOS work a lot more like Android, which already allows for alternative app stores and side loading, and is therefore not subject to these legal actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ruling’s broader effect on the software world
&lt;/h2&gt;

&lt;p&gt;Although these court proceedings have centered around Apple, this ruling has consequences that could extend to software distribution on other platforms, because there are a &lt;em&gt;lot&lt;/em&gt; of platforms that have monopolistic control over app distribution that you might not think about.&lt;/p&gt;

&lt;p&gt;Consider Rokus, Fire TVs, and basically all smart TVs platforms. In each of these, the only way to get apps is through a single vendor-controlled store.&lt;/p&gt;

&lt;p&gt;Here’s another interesting one: Microsoft &lt;a href="https://www.theguardian.com/games/2019/apr/16/discless-xbox-one-release-date-price"&gt;recently announced an Xbox that doesn’t come with a disc drive&lt;/a&gt;. Presumably the only way to purchase games for this new Xbox will be through a Microsoft-provided store; therefore, does that new Xbox violate US anti-trust law?&lt;/p&gt;

&lt;p&gt;These cases will depend on how each platform makes money. For example, if Microsoft doesn’t make any additional money from digital purchases, there’s not much reason for developers or customers to file a lawsuit. But the court’s recent decision does open the door for class-action lawsuits against platforms with a business model similar to Apple’s. &lt;/p&gt;

&lt;h2&gt;
  
  
  Quick takeaways
&lt;/h2&gt;

&lt;p&gt;If you’re still confused by all of this here’s a quick wrap up: the US Supreme Court ruled that customers have the ability to sue Apple for its monopolistic control over iOS app distribution, alleging that this monopoly unfairly raises prices for consumers.&lt;/p&gt;

&lt;p&gt;This ruling doesn’t have any immediate effect on iOS users or developers, but it allows for further lawsuits to proceed—lawsuits that could fundamentally change how iOS app distribution works, and could have a broader effect on how all platforms control app distribution.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>ios</category>
    </item>
    <item>
      <title>How to Build a PWA, an iOS App, and an Android App—From One Codebase</title>
      <dc:creator>TJ VanToll</dc:creator>
      <pubDate>Tue, 30 Oct 2018 14:44:51 +0000</pubDate>
      <link>https://forem.com/progress/how-to-build-a-pwa-an-ios-app-and-an-android-appfrom-one-codebase-50nc</link>
      <guid>https://forem.com/progress/how-to-build-a-pwa-an-ios-app-and-an-android-appfrom-one-codebase-50nc</guid>
      <description>&lt;p&gt;I have a long history of choosing between web and native, often wrongly. I’ve built web apps that were scraped for native apps, and I’ve wasted time building native apps that found no audience.&lt;/p&gt;

&lt;p&gt;In my current job as a mobile-focused developer advocate at &lt;a href="https://www.progress.com/" rel="noopener noreferrer"&gt;Progress&lt;/a&gt;, I talk to developers that regret their web or native decision every week. Sometimes you don’t realize you need a native app until you hit the limitations of the web, and conversely, sometimes you realize a web app meets your needs only after you’ve went through the lengthy process of building multiple native apps.&lt;/p&gt;

&lt;p&gt;The good news is JavaScript developers no longer have to make this difficult choice. Through the use of the recently announced &lt;a href="https://blog.angular.io/apps-that-work-natively-on-the-web-and-mobile-9b26852495e7" rel="noopener noreferrer"&gt;NativeScript and Angular integration&lt;/a&gt;, it’s now quite easy to build a PWA (Progressive Web App), a native iOS app, and a native Android app from one codebase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fts1vqksk1rfr97qvs2q3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fts1vqksk1rfr97qvs2q3.png" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article I’ll show you how it works. You’ll learn the steps you’ll need to take to build for all three platforms, as well as some tips and tricks I learned from going through this process myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’re building
&lt;/h2&gt;

&lt;p&gt;Over the last month I built a Pokémon-based checklist app and deployed it to Google Play, the iOS App Store, and &lt;a href="https://shinydex.app" rel="noopener noreferrer"&gt;the web&lt;/a&gt;. The app is a purposely simple app designed to help teach how the NativeScript and Angular technology stack work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbx0yd55dpnqfc4zf992.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbx0yd55dpnqfc4zf992.png" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this article I’ll walk you through building a similar checklist-style app that looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb20euoym88fuo107zo3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb20euoym88fuo107zo3.gif" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to follow along as a way of starting your own code-sharing application, or to just browse the code to get a high-level idea of how this whole process works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting your app
&lt;/h2&gt;

&lt;p&gt;When you’re ready to build, your first step is to install the Angular CLI, NativeScript CLI, and NativeScript schematics, all of which are available on npm. Open your terminal or command prompt and run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @angular/cli
npm install -g nativescript
npm install -g @nativescript/schematics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what these tools do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://cli.angular.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Angular CLI&lt;/strong&gt;&lt;/a&gt; is a command-line interface for building and running &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; apps.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nativescript.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;NativeScript&lt;/strong&gt;&lt;/a&gt; is an open-source framework for building iOS and Android apps with JavaScript or TypeScript.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NativeScript/nativescript-schematics" rel="noopener noreferrer"&gt;&lt;strong&gt;NativeScript schematics&lt;/strong&gt;&lt;/a&gt; is an Angular CLI extension that adds the ability to do NativeScript-related things. As you’ll see in a minute, this is what makes it possible to run your Angular apps on iOS and Android.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With installation out of the way, your next step is to create an app. To do that, run the following command from your terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new Checklist --collection @nativescript/schematics --shared --sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down what’s happening here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ng new&lt;/code&gt; is the Angular CLI command you use to start new Angular apps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Checklist&lt;/code&gt; is your app name. For you own apps you’ll want to provide your own value here.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--collection @nativescript/schematics&lt;/code&gt; flag tells the Angular CLI about NativeScript schematics, which makes it possible for this app to run on iOS and Android through NativeScript.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--shared&lt;/code&gt; flag tells NativeScript schematics you wish to start with a code-sharing project template.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--sample&lt;/code&gt; flag has NativeScript schematics scaffold a few sample components which we’ll look at momentarily. You’ll want to omit this flag when you go to build your own apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that you have an app let’s look at how to run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running your app
&lt;/h2&gt;

&lt;p&gt;First, &lt;code&gt;cd&lt;/code&gt; into the new app you just built.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd Checklist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, run &lt;code&gt;npm install&lt;/code&gt; to install your app’s required dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here there are three different commands you can use to run your app.&lt;/p&gt;

&lt;p&gt;First, &lt;code&gt;ng serve&lt;/code&gt; is how you run your newly created app on the web. Go ahead and run this command from your terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the command finishes, you’ll see a message about an Angular Live Development Server listening.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fp6nehlkpcx62jcp6xq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fp6nehlkpcx62jcp6xq.png" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you follow the instructions and visit &lt;code&gt;localhost:4200&lt;/code&gt; in your browser you’ll see the default web app running, which is a simple master-detail app showing soccer players.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc99gsf5zm6qmzj5oi81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc99gsf5zm6qmzj5oi81.png" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve done any Angular development before this will feel very familiar, as it’s the same workflow you use to build Angular web apps—which is cool!&lt;/p&gt;

&lt;p&gt;But of course you’re not building only for the web with this workflow, and the real magic happens when you bring NativeScript into the picture.&lt;/p&gt;

&lt;p&gt;But before you run this app on iOS and Android there’s one warning I need to give: because NativeScript apps are truly native iOS and Android apps, there are an additional set of system requirements you need to install on your development machine to build these apps. Check out &lt;a href="https://docs.nativescript.org/angular/start/general-requirements" rel="noopener noreferrer"&gt;this page on the NativeScript docs&lt;/a&gt; for the necessary setup steps you’ll need to complete.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The next version of NativeScript, NativeScript 5.0, has some fun changes that will allow you to run apps without any local setup. You can &lt;a href="https://github.com/NativeScript/nativescript-cli/issues/3813" rel="noopener noreferrer"&gt;learn more on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the setup out of the way, return to your terminal or command prompt, use &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;C&lt;/code&gt; to stop your &lt;code&gt;ng serve&lt;/code&gt; command, and next execute &lt;code&gt;npm run android&lt;/code&gt;. The command will take a minute to run, as under the hood NativeScript is building a completely native Android app. When it finishes you’ll see the following screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q605x55o95oyk28aob3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q605x55o95oyk28aob3.png" width="225" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re developing on macOS, you can also try running &lt;code&gt;npm run ios&lt;/code&gt;, which runs through a similar process, but instead builds and starts your app on iOS. When it finishes you’ll see this screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa84h6gewwlfgkwjm4mej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa84h6gewwlfgkwjm4mej.png" width="208" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a single codebase and a set of straightforward commands you now have the same app running in three places.&lt;/p&gt;

&lt;p&gt;Cool, huh?&lt;/p&gt;

&lt;p&gt;Now that you know how to run your app, let’s dig into your app’s code to see what’s going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking through the code
&lt;/h2&gt;

&lt;p&gt;Starting at the root of your project, here are the top-level folders you need to know about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checklist/
├── App_Resources
├── platforms
└── src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;App_Resources&lt;/code&gt; folder is where NativeScript stores iOS and Android configuration files and image resources, such as your app’s icons and splash screens. Later in your app development, you’ll want to switch to your own image assets using the NativeScript CLI’s &lt;code&gt;tns resources generate&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;platforms&lt;/code&gt; folder is where NativeScript stores your generated iOS and Android apps; you can think of it as a &lt;code&gt;dist&lt;/code&gt; folder for your native projects.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;src&lt;/code&gt; folder is where your source code lives, and it’s where you’ll be spending 95% of your time.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: There are a bunch of other configuration files in the root folder, such as an &lt;code&gt;angular.json&lt;/code&gt; file for Angular CLI customization, a &lt;code&gt;package.json&lt;/code&gt; file for managing dependencies, and a series of &lt;code&gt;tsconfig.json&lt;/code&gt; files for configuring TypeScript. When getting started it’s best to leave these files alone; you can come back to them later and customize them to meet your project’s needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since the &lt;code&gt;src&lt;/code&gt; folder is where you’ll be spending most of your time, let’s dig into the contents of that folder in more detail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── app
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.tns.html
│   ├── app.component.ts
│   ├── app.module.tns.ts
│   ├── app.module.ts
│   ├── app.routes.ts
│   ├── app.routing.tns.ts
│   ├── app.routing.ts
│   └── barcelona
│       └── ...
├── app.css
├── assets
├── index.html
├── main.tns.ts
├── main.ts
├── package.json
└── styles.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’ve built Angular web apps before a lot of this structure will look very familiar. All Angular apps have a &lt;code&gt;main.ts&lt;/code&gt; file for initialization, an &lt;code&gt;app.module.ts&lt;/code&gt; file for &lt;a href="https://angular.io/guide/ngmodules" rel="noopener noreferrer"&gt;module declarations&lt;/a&gt;, a series of &lt;code&gt;app.routing.ts&lt;/code&gt; files for &lt;a href="https://angular.io/guide/router" rel="noopener noreferrer"&gt;setting up routing&lt;/a&gt;, and an &lt;code&gt;app.component.ts&lt;/code&gt; file to use as the &lt;a href="https://angular.io/guide/entry-components" rel="noopener noreferrer"&gt;app’s first component&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: If you’re new to Angular and would like a more in-depth introduction to these concepts, check out the &lt;a href="https://angular.io/guide/quickstart" rel="noopener noreferrer"&gt;Angular quick-start tutorial&lt;/a&gt;. All of the concepts you learn there directly apply to the code-sharing structure in this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One concept unique to NativeScript’s code-sharing workflow is the &lt;code&gt;.tns&lt;/code&gt; naming convention you see on some of your app’s files (for example &lt;code&gt;app.routing.tns.ts&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;By default, NativeScript schematics includes all your project’s files in both your web and mobile apps—after all, you are trying to share code. However, in certain situations you will need to create web- and mobile-specific files, and that’s where the &lt;code&gt;.tns&lt;/code&gt; extension comes in.&lt;/p&gt;

&lt;p&gt;For example, take the &lt;code&gt;app.module.ts&lt;/code&gt; and &lt;code&gt;app.module.tns.ts&lt;/code&gt; files. When you run your app on the web, the Angular CLI uses your &lt;code&gt;app.module.ts&lt;/code&gt; file as you’d expect. However, when you run your app on iOS or Android, NativeScript schematics instead grabs and uses your &lt;code&gt;app.module.tns.ts&lt;/code&gt; file. This convention is a powerful way to split your web and mobile code as necessary, and you’ll use it frequently as you build your own apps using this setup.&lt;/p&gt;

&lt;p&gt;Now that you have a bit of background on how your project is structured, let’s build something new.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building your web UI
&lt;/h2&gt;

&lt;p&gt;In the starter app, the vast majority of your code is in a &lt;code&gt;src/app/barcelona&lt;/code&gt; folder, as that’s the code that builds up the player list you saw in your app earlier. In this section you’re going to create a brand-new component for the web, and in the next section, you’ll see just how easy it is to get that same component working in a native iOS and Android app.&lt;/p&gt;

&lt;p&gt;Let’s start by scaffolding some files. To do so, start by using the &lt;code&gt;cd&lt;/code&gt; command to navigate to your &lt;code&gt;src/app&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /src/app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, make a new folder named &lt;code&gt;list&lt;/code&gt;, and create the following files in that folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. app
└── list
    ├── list.common.ts
    ├── list.component.css
    ├── list.component.html
    ├── list.component.ts
    ├── list.module.ts
    └── list.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: When you get more comfortable working with NativeScript schematics, there are a series of commands you can use to help generate components and modules. See the &lt;a href="https://github.com/NativeScript/nativescript-schematics#generating-components-modules-directives-etc" rel="noopener noreferrer"&gt;NativeScript schematics documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s what to put in those files as a first step. Don’t worry too much about exactly what this code is doing, as we’ll discuss the important things to note momentarily.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;list.common.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;COMPONENT_DECLARATIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;ListComponent&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PROVIDERS_DECLARATIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;ListService&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListComponent&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;list.component.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#C0C0C0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nc"&gt;.selected&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#C0C0C0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;96px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;96px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;list.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let item of items"&lt;/span&gt; &lt;span class="na"&gt;[class.selected]=&lt;/span&gt;&lt;span class="s"&gt;"item.selected"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"itemTapped(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;[src]=&lt;/span&gt;&lt;span class="s"&gt;"item.image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ item.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;list.component.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;listService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;itemTapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggleSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;list.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClientModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;COMPONENT_DECLARATIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PROVIDERS_DECLARATIONS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;HttpClientModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;RouterModule&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;COMPONENT_DECLARATIONS&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;PROVIDERS_DECLARATIONS&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;list.service.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rawgit.com/tjvantoll/ShinyDex/master/assets/151.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returnData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;returnData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;returnData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;toggleSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, don’t worry if you don’t understand everything that’s going on in this code. For now, all you need to know is that this is a fairly straightforward Angular component that loads data from an API and shows it in a list.&lt;/p&gt;

&lt;p&gt;To activate this new component so you can try it out, first, add this new component to your &lt;code&gt;app.module.ts&lt;/code&gt; file so Angular knows about it. Here’s what your new &lt;code&gt;app.module.ts&lt;/code&gt; file should look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppRoutingModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.routing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list/list.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;BrowserModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;AppRoutingModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ListModule&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, change the default path in your &lt;code&gt;app.routes.ts&lt;/code&gt; file, so Angular navigates to the new list component by default. The new &lt;code&gt;app.routes.ts&lt;/code&gt; file should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;redirectTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pathMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, just so this app looks a little nicer, paste the following code in your &lt;code&gt;src/styles.css&lt;/code&gt; file, which is the place you add global CSS for your Angular apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this in place, return to your terminal or command prompt and run &lt;code&gt;ng serve&lt;/code&gt;. After the command runs, open your browser and visit &lt;code&gt;localhost:4200&lt;/code&gt;, and you should now see a simple little checklist that looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyejwtegszu8f63m9dbij.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyejwtegszu8f63m9dbij.gif" width="225" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app you now have is a really simple app that allows users to select items. If you look at the code in &lt;code&gt;list.service.ts&lt;/code&gt;, you can see that the app also remembers the user’s selections using &lt;code&gt;localStorage&lt;/code&gt;—meaning, all selections remain when the user returns to the app.&lt;/p&gt;

&lt;p&gt;At this point you have a very simple mobile app. If you’d like, you could follow one of many online guides for &lt;a href="https://www.smashingmagazine.com/2018/09/pwa-angular-6/" rel="noopener noreferrer"&gt;giving this app a service worker and making it into a Progressive Web App&lt;/a&gt;—after all, at the moment you’re just using the Angular CLI to build a web app.&lt;/p&gt;

&lt;p&gt;The real fun of this workflow though is in just how easy it is to turn a functional app like this into a native iOS and Android app. Let’s look at how to do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating your iOS and Android app
&lt;/h2&gt;

&lt;p&gt;When converting a web interface to mobile using NativeScript schematics, your first task is to identify which code you can reuse and which code you cannot. Let’s return to the list of files that make up this component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. app
└── list
    ├── list.common.ts
    ├── list.component.css
    ├── list.component.html
    ├── list.component.ts
    ├── list.module.ts
    └── list.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this example you’ll need to create a NativeScript-specific markup file (&lt;code&gt;.html&lt;/code&gt;), styling file (&lt;code&gt;.css&lt;/code&gt;), and module file (&lt;code&gt;.module.ts&lt;/code&gt;). To do that, to ahead and create three new files, &lt;code&gt;list.component.tns.css&lt;/code&gt;, &lt;code&gt;list.component.tns.html&lt;/code&gt;, and &lt;code&gt;list.module.tns.ts&lt;/code&gt;. Your file tree should now look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. app
└── list
    ├── list.common.ts
    ├── list.component.css
    ├── list.component.html
    ├── list.component.tns.css (new)
    ├── list.component.tns.html (new)
    ├── list.component.ts
    ├── list.module.ts
    ├── list.module.tns.ts (new)
    └── list.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that the Angular CLI will automatically grab the code in your &lt;code&gt;.tns.*&lt;/code&gt; files when building for NativeScript, and your non-&lt;code&gt;.tns&lt;/code&gt; files when building for the web. Therefore, the &lt;code&gt;.tns&lt;/code&gt; files are where you need to put your code that’s specific to NativeScript.&lt;/p&gt;

&lt;p&gt;To do that, start by opening your &lt;code&gt;list.module.tns.ts&lt;/code&gt; file and paste in the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NO_ERRORS_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NativeScriptCommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nativescript-angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NativeScriptHttpClientModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nativescript-angular/http-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NativeScriptRouterModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nativescript-angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;COMPONENT_DECLARATIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PROVIDERS_DECLARATIONS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;NativeScriptCommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;NativeScriptHttpClientModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;NativeScriptRouterModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;NativeScriptRouterModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;NativeScriptRouterModule&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;COMPONENT_DECLARATIONS&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;PROVIDERS_DECLARATIONS&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;NO_ERRORS_SCHEMA&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need a NativeScript-specific module file so that you can declare NativeScript-specific imports, such as &lt;code&gt;NativeScriptHttpClientModule&lt;/code&gt;, and &lt;code&gt;NativeScriptRouterModule&lt;/code&gt;. However, note how both your &lt;code&gt;list.module.ts&lt;/code&gt; and &lt;code&gt;list.module.tns.ts&lt;/code&gt; files pull routes, declarations, and providers from your &lt;code&gt;list.common.ts&lt;/code&gt; file. This gives you the ability to add those declarations in one place, without having to change two different module files every time you need to make a small update.&lt;/p&gt;

&lt;p&gt;The next file to change is your app’s markup file. To do that, open your &lt;code&gt;list.component.tns.html&lt;/code&gt; file and paste in the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ListView&lt;/span&gt; &lt;span class="na"&gt;[items]=&lt;/span&gt;&lt;span class="s"&gt;"items"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;let-item=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;FlexboxLayout&lt;/span&gt; &lt;span class="na"&gt;[class.selected]=&lt;/span&gt;&lt;span class="s"&gt;"item.selected"&lt;/span&gt; &lt;span class="na"&gt;(tap)=&lt;/span&gt;&lt;span class="s"&gt;"itemTapped(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;[src]=&lt;/span&gt;&lt;span class="s"&gt;"item.image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;[text]=&lt;/span&gt;&lt;span class="s"&gt;"item.name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/Label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/FlexboxLayout&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ListView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: Need help learning these NativeScript user interface components? Try &lt;a href="https://play.nativescript.org/" rel="noopener noreferrer"&gt;NativeScript Playground&lt;/a&gt;, and specifically, try out the components pane on the bottom-left-hand side of the screen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, to style these components, paste the following code into your &lt;code&gt;list.component.tns.css&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;FlexboxLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.selected&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#C0C0C0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;Image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: If you’re a fan of SASS, you can use it with NativeScript schematics and share CSS variables such as colors. Check out the &lt;a href="https://docs.nativescript.org/angular/code-sharing/creating-a-new-project#sass" rel="noopener noreferrer"&gt;instructions here on the NativeScript documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With all this in place, your last step is to import your &lt;code&gt;ListModule&lt;/code&gt; in your &lt;code&gt;app.module.tns.ts&lt;/code&gt; file, exactly like you did with your &lt;code&gt;app.module.ts&lt;/code&gt; file. To do so, replace the contents of your &lt;code&gt;app.component.tns.ts&lt;/code&gt; file with the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NO_ERRORS_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NativeScriptModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nativescript-angular/nativescript.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppRoutingModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.routing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list/list.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;AppComponent&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;NativeScriptModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;AppRoutingModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ListModule&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;AppComponent&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;NO_ERRORS_SCHEMA&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, you should have a functioning NativeScript app, right? Actually there’s one last change you need to make, and to show it let’s introduce the concept of helper files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using helper files
&lt;/h2&gt;

&lt;p&gt;When taking a code-sharing approach, sometimes you need to completely split your implementations for web and native. Your user interfaces for example always need different implementations, as you need to use DOM nodes for the web, and NativeScript user-interface controls for mobile.&lt;/p&gt;

&lt;p&gt;However, there are often times where you can share almost all of your code, but need slightly different implementations for the web and mobile. There’s actually one example of this in your sample app, and it’s in your &lt;code&gt;list.service.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you open &lt;code&gt;list.service.ts&lt;/code&gt;, you’ll see two different references to &lt;code&gt;localStorage&lt;/code&gt;—one in the constructor...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and another in the &lt;code&gt;save()&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem here is &lt;code&gt;localStorage&lt;/code&gt; is a browser API. It works great on the web, but it does not exist in NativeScript apps because NativeScript apps do not run in a browser.&lt;/p&gt;

&lt;p&gt;You have a few different ways you could handle this. You could create a &lt;code&gt;list.service.tns.ts&lt;/code&gt; file, and create a separate implementation of this service that works for your mobile app. However, if you do that you need to duplicate a lot of code that is the same across both platforms, such as the code that calls your backend over HTTP and parses the data.&lt;/p&gt;

&lt;p&gt;When you hit these scenarios another option you have is to create helper files. That is, create two files with identical APIs, and put your web implementation of those APIs in one file, and your NativeScript implementation of those APIs in another.&lt;/p&gt;

&lt;p&gt;To do this for your service create two new files named &lt;code&gt;list.helper.ts&lt;/code&gt; and &lt;code&gt;list.helper.tns.ts&lt;/code&gt;. Your new folder structure should now look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. app
└── list
    ├── list.common.ts
    ├── list.component.css
    ├── list.component.html
    ├── list.component.tns.css
    ├── list.component.tns.html
    ├── list.component.ts
    ├── list.helper.tns.ts (new)
    ├── list.helper.ts (new)
    ├── list.module.ts
    ├── list.module.tns.ts
    └── list.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: In a real-world app you would probably want to move the service and its helper files to its own folder, both to break up the folder structure, and to make the service reusable. For this tutorial though it’s easiest for us to keep everything in one place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open your &lt;code&gt;list.helper.ts&lt;/code&gt; file and paste in the following code. This is the same &lt;code&gt;localStorage&lt;/code&gt; code you had in your service extracted into a helper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListHelper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;readItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;writeItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, open your &lt;code&gt;list.helper.tns.ts&lt;/code&gt; file and paste in the following code. This code follows the same API as the web helper, but instead uses some of NativeScript’s built-in modules to accomplish the same task for your iOS and Android apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;knownFolders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tns-core-modules/file-system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListHelper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;saveFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saveFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;knownFolders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;readItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saveFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readTextSync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;writeItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saveFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your last step here is changing your service to utilize these new helpers, which you can do by replacing the code in your &lt;code&gt;list.service.ts&lt;/code&gt; file with the code below, which makes use of the new helpers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListHelper&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./list.helper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;helper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListHelper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ListHelper&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rawgit.com/tjvantoll/ShinyDex/master/assets/151.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returnData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;returnData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;returnData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;toggleSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have all your code in place, go ahead and run your app on iOS or Android using one of the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run ios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an app that looks something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb20euoym88fuo107zo3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb20euoym88fuo107zo3.gif" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although this app is simple it’s important to remember what you’re seeing here. These are &lt;em&gt;native&lt;/em&gt; iOS and Android apps, using native iOS and Android user interface controls. And not only did you build these apps with Angular and TypeScript, you even shared a good chunk the code behind this app with your web app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can learn more about using helper files to split your code from &lt;a href="https://docs.nativescript.org/angular/code-sharing/code-splitting#partial-differences" rel="noopener noreferrer"&gt;this article on the NativeScript documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The big picture
&lt;/h2&gt;

&lt;p&gt;In this article we looked at how to build a simple component that shares code across the web and native apps. Although we focused on one component for this article, the approach is flexible enough to build apps of any scale.&lt;/p&gt;

&lt;p&gt;Depending on your needs, you might want to create relatively identical apps for both the web and mobile. Or, you might want to create very different apps that share the same language, framework, underlying infrastructure, and service layer.&lt;/p&gt;

&lt;p&gt;After all, the power from using NativeScript schematics is not just code sharing—you also gain a ton from using one language and one framework to build for three platforms. Hopefully you’re as excited as me about the awesome things you can build with this technology stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.nativescript.org/angular/code-sharing/intro" rel="noopener noreferrer"&gt;NativeScript documentation on code sharing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tjvantoll/shinydex/tree/article" rel="noopener noreferrer"&gt;Final source code for this article’s sample&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tjvantoll/ShinyDex" rel="noopener noreferrer"&gt;Full source code of ShinyDex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mobile</category>
      <category>pwa</category>
      <category>angular</category>
      <category>nativescript</category>
    </item>
  </channel>
</rss>
