Back to Blog Home
← all posts

Using 3rd-party iOS Frameworks in your NativeScript Apps

May 12, 2015 — by Nikolay Diyanov

The NativeScript framework already provides a fairly comprehensive set of standard UI controls that you need to build an app. But what if you want to build an app with the NativeScript framework using a 3rd-party library? We’ve got you covered. With the NativeScript framework you can effortlessly take a native iOS or Android library and start using it with JavaScript. As an example, let’s look at how to build a simple Seismograph app with the NativeScript framework and Telerik Chart for iOS. Here’s what the final result looks like:
sample-ios-seismograph-nativescript

Support for Native iOS Libraries

A week ago we released a new version of Telerik UI for iOS] which now includes embedded frameworks. Thanks to the way NativeScript processes such libraries and exposes their API in JavaScript, we can guarantee 0-day support for the latest native frameworks.

It should be noted that the embedded frameworks come in two forms: Debug and Release. The Debug version of the embedded framework allows you to use it with both simulators and real devices. However, Apple does not accept apps which contain Simulator slices, and here comes the need for a Release version. The Release version only supports real devices. So, for a start we’ll use the Debug version which will allow us to test the app and on a simulator and device. When the deployment time comes, the Debug framework should be replaced with the Release framework.

You can get a Trial of the latest Telerik UI for iOS from here

Last, but not least, at the moment in order to use a 3rd-party iOS library, you need a Mac, but this will be changing shortly, as the Telerik AppBuilder clients will soon have the ability to load 3rd-party frameworks and plugins into NativeScript apps. Because Telerik AppBuilder performs iOS builds in the cloud, you’ll be able to use these 3rd-party libraries in NativeScript apps on any OS.
 

The NativeScript CLI setup

At the moment, in order to use a 3rd-party library in your NativeScript iOS project, you need to work the NativeScript Command Line Interface (CLI). To get these tools, first you need the NPM package manager, which you can download from:https://nodejs.org/download/ 

After you install the NPM package manager, open a Terminal instance and write the following command to install the NativeScript Command Line Interface:

npm install -g nativescript

Tip: If elevated privileges are required, try with the following command:

sudo npm install - nativescript

For a more detailed explanation of how to install the NativeScript CLI, check out this article.

The project setup

Creating the project

  1. Using the navigation commands in the Terminal, navigate to the directory where you would like to create the project and then write:

    tns create Seismograph

    where Seismograph is the name of our application. This creates a folder named Seismograph at which contains our project files. 
  2. Enter the Seismograph directory:

    cd seismograph

  3. Create the iOS project template that will be used by the NativeScript framework and the NativeScript project. To do so, run the following command:

    tns platform add ios
You can find the iOS project template at Seismograph/platforms/ios

Adding the native library

It’s now time to add the embedded iOS framework of UI for iOS to the project, so that you can use this framework in your NativeScript project. Assuming that the Debug version of the embedded framework is located at /Users/[USER NAME]/Documents/Telerik/UI for iOS Q1 2015/embedded/TelerikUI/Debug, and that your current Terminal directory is Seismograph, the command should be:

tns library add ios “/Users/[USER NAME]/Documents/Telerik/UI for iOS Q1 2015/embedded/TelerikUI/Debug/TelerikUI.framework”

Let's jump to your favorite text editor and start coding!

Coding the app

Removing the boilerplate sample code

In order to improve your getting started experience and speed up your development with the NativeScript framework, the project that you have created steps on top of a sample app with a button and a label. We don’t really need these at the moment, so let’s remove the unnecessary content:

  1. Go to app → main-page.xml and remove the contents between the Page opening and closing tags.
  2. Go to app → main-page.js and remove the content.
  3. Delete the app → main-view-model.js file
  4. Delete the contents of the app.css file.
Now our project is a white canvas on which we can build our Seismograph application.

The Seismograph code

The Seismograph app will consist of the Telerik Chart for iOS and three buttons - to start, stop, and reset the seismograph functionality. Therefore, we can use the following layout:

<Page loaded="onPageLoaded" xmlns="http://www.nativescript.org/tns.xsd">
    <GridLayout margin="20" columns="auto, *">
        <StackLayout orientation="vertical" dock="top">
          <Button id="Start" tap="tapStart" text="Start"/>
          <Button id="Stop" tap="tapStop" text="Stop"/>
          <Button id="Reset" tap="tapReset" text="Reset"/>
        </StackLayout>
    <Placeholder creatingView="creatingChart" col="1"/>
    </GridLayout>
</Page>

Here we can see that we declare the need for onPageLoaded, tapStart, tapStop, tapReset functions that we will add later on in your main-page.js file.

You can notice that we do not explicitly declare a Chart type here. We can afford to do so, because of the convenience that the Placeholder widget gives us. Thanks to it we can quickly load any native component without creating a wrapper for it. Here is the implementation of the creatingChart method that is called on the creatingView event:

var chart;
  
function creatingChart(args) {
    chart = TKChart.new();
    args.view = chart;
}
  
exports.creatingChart = creatingChart;

It should be noted that the Placeholder widget, together with the lack of any wrapper are most suitable for a single platform project. You can even notice the existence of Objective-C notation for creating a new TKChart instance, where TKChart is the type of the native iOS chart. In cross-platform projects, it’s best to create a common wrapper first and then use it directly in the xml. However, this is a subject for another blog article.

Now that we have our Chart in place, it’s time to feed it with some accelerometer data. To do so, we will use the dedicated Apple CoreMotion API. Of course, despite the fact we are working with native APIs, it is all JavaScript that we are coding in.

  1. First, let’s create an instance of the CMMotionManager iOS class responsible for getting the data from the accelerometer sensor. Set the interval at which it returns data to 0.2 in the onPageLoaded method:

    var motionManager = new CMMotionManager();
      
    function onPageLoaded(args) {
        motionManager.accelerometerUpdateInterval = 0.2;
      
        startOperations();
    }
      
    exports.onPageLoaded = onPageLoaded;

    Here we call a startOperations() method the purpose of which is explained below.

  2. The startOperations checks if there is an accelerometer sensor on the device and if so, returns updates coming from the accelerometer every 0.2 seconds. Here is the method implementation:

    var coefficient = 1000;
      
    function startOperations() {
        if(motionManager.accelerometerAvailable)
        {
            motionManager.startAccelerometerUpdatesToQueueWithHandler(NSOperationQueue.currentQueue(),
                function (accelerometerData, error) {
                    var acceleration = accelerometerData.acceleration;
      
                    buildChartWithPoints(acceleration.x*coefficient);
                }
            );
        }
    }
      
    exports.startOperations = startOperations;

    Every time an update is sent we call the buildChartWithPoints method to build the chart and fill it with data as per the native Chart requirements. We apply a coefficient to the values we give to the chart, so that the Chart can show both small and big values in one plot area without zooming in/out.

  3. It’s now time to discuss how the buildChartWithPoints function is implemented.

    var dataPoints = [];
      
    function buildChartWithPoints(point) {
    //implementation here
    }

    1. Every time an update is sent, we need to reset the Chart. To do so, we need to remove all the data and annotations it currently has:

      chart.removeAllData();
      chart.removeAllAnnotations();
    2. Further, the dataPoints that are added to the Chart should be plotted against the x-axis depending on the time of the update, and against the y-axis depending on the magnitude of the acceleration. And, if there are 26 data points already added in the Chart, we should remove the first measured point from the Chart, making the measured data “flow”.

      var dataPoint = TKChartDataPoint.alloc().initWithXY(NSDate.date(), point);
        
      dataPoints.push(dataPoint);
        
      if (dataPoints.length > 25) {
          dataPoints.shift();
      }
    3. It’s time to set up the y-axis. It’s an axis with a range (-1000; 1000) and it should intersect with the x-axis at the 0 value. Here is how to do that:

      var yAxis = TKChartNumericAxis.alloc().initWithMinimumAndMaximum(-coefficient, coefficient);
      yAxis.position = TKChartAxisPosition.Left;
      yAxis.majorTickInterval = 200;
      yAxis.minorTickInterval = 1;
      yAxis.offset = 0;
      yAxis.baseline = 0;
      yAxis.style.labelStyle.fitMode = TKChartAxisLabelFitMode.Rotate;
      yAxis.style.labelStyle.firstLabelTextAlignment = TKChartAxisLabelAlignment.Left;
      chart.yAxis = yAxis;
    4. Next, we should add the Chart line series. We first initiate them, passing the dataPoints array. Then we set the color of the line to red using the appropriate native API. Finally, we add the series to the chart:

      var lineSeries = TKChartLineSeries.alloc().initWithItems(dataPoints);
      lineSeries.style.palette = new TKChartPalette();
      var strokeRed = TKStroke.strokeWithColor(UIColor.colorWithRedGreenBlueAlpha(1, 0, 0, 1));
      strokeRed.width = 1.5;
      lineSeries.style.palette.addPaletteItem(TKChartPaletteItem.paletteItemWithDrawables([strokeRed]));
      chart.addSeries(lineSeries);

      Adding data points with date-time values for the x-axis will automatically create DateTime axis for the Chart.

    5. We can change the style of the pre-created x-axis, hiding the labels and ticks, using the following code:
      var axisColor = TKStroke.strokeWithColor(UIColor.blackColor());
      axisColor.width = 1;
      chart.xAxis.style.lineStroke = axisColor;
      chart.xAxis.style.majorTickStyle.ticksHidden = true;
      chart.xAxis.style.labelStyle.textHidden = true;
    6. To highlight the magnitude of a measured value, let’s add a few annotations - two dashed line annotations at -150 and 150 and green/yellow/red band annotations outlining small/little/big deviations:

      var annotationBandRed = TKChartBandAnnotation.alloc().initWithRangeForAxis(TKRange.alloc().initWithMinimumAndMaximum(-1000, 1000), chart.yAxis);
      annotationBandRed.style.fill = TKSolidFill.solidFillWithColor(UIColor.colorWithRedGreenBlueAlpha(255/255.0, 149/255.0, 149/255.0, 0.7));
      chart.addAnnotation(annotationBandRed);
        
      var annotationBandYellow = TKChartBandAnnotation.alloc().initWithRangeForAxis(TKRange.alloc().initWithMinimumAndMaximum(-500, 500), chart.yAxis);
      annotationBandYellow.style.fill = TKSolidFill.solidFillWithColor(UIColor.colorWithRedGreenBlueAlpha(252/255.0, 255/255.0, 138/255.0, 0.7));
      chart.addAnnotation(annotationBandYellow);
        
      var annotationBandGreen = TKChartBandAnnotation.alloc().initWithRangeForAxis(TKRange.alloc().initWithMinimumAndMaximum(-300, 300), chart.yAxis);
      annotationBandGreen.style.fill = TKSolidFill.solidFillWithColor(UIColor.colorWithRedGreenBlueAlpha(152/255.0, 255/255.0, 149/255.0, 1));
      chart.addAnnotation(annotationBandGreen);
        
      var dashStroke = TKStroke.strokeWithColor(UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.5));
      dashStroke.dashPattern = ([6, 1]);
      dashStroke.width = 0.5;
        
      var positiveDashAnnotation = TKChartGridLineAnnotation.alloc().initWithValueForAxis(150, chart.yAxis);
      positiveDashAnnotation.style.stroke = dashStroke;
      chart.addAnnotation(positiveDashAnnotation);
        
      var negativeDashAnnotation = TKChartGridLineAnnotation.alloc().initWithValueForAxis(-150, chart.yAxis);
      negativeDashAnnotation.style.stroke = dashStroke;
      chart.addAnnotation(negativeDashAnnotation)
    7. At the bottom of the BuildChartWithPoint function we add a custom needle annotation which “writes” the line on the chart. Below we just show how to use the custom annotation. In the next paragraph you can see how to implement the annotation itself:

      if(dataPoints.length > 1) {
        var needle = NeedleAnnotation.alloc().initWithXYForSeries(dataPoint.dataXValue, dataPoint.dataYValue, lineSeries);
        needle.zPosition = TKChartAnnotationZPosition.AboveSeries;
        chart.addAnnotation(needle);
      }

      With this, the implementation of the buildChartWithPoint method is done.
  4. The custom needle annotation can be implemented in a new js file which we can name needle-annotation.js:

    var NeedleAnnotation = TKChartPointAnnotation.extend({
        layoutInRect: function(bounds)
        {
            var xval = this.series.xAxis.numericValue(this.position.dataXValue);
            var x = TKChartSeriesRender.locationOfValueForAxisInRect(xval, this.series.xAxis, bounds);
            var yval = this.series.yAxis.numericValue(this.position.dataYValue);
            var y = TKChartSeriesRender.locationOfValueForAxisInRect(yval, this.series.yAxis, bounds);
            center = CGPointMake(x, y);
        },
      
        drawInContext: function(context)
        {
            CGContextBeginPath(context);
            CGContextMoveToPoint(context, center.x-20, center.y);
            CGContextAddLineToPoint(context, center.x+20, center.y+20);
            CGContextAddLineToPoint(context, center.x+20, center.y-20);
                 
            CGContextSetRGBFillColor(context, 0, 0, 0, 1);
            CGContextFillPath(context);
        }
    });
      
    exports.NeedleAnnotation = NeedleAnnotation;
  5. Then, in order to use that implementation in the buildChartWithPoint function, we should load the needle-annotation.js file at the top of the main-page.js file the following way:

    var needleAnnotation = require("./needle-annotation");
    var NeedleAnnotation = needleAnnotation.NeedleAnnotation;
  6. Next, we should add the buttons’ implementations for controlling the chart operations:

    function tapStart() {
        startOperations();
    }
      
    function tapStop() {
        motionManager.stopAccelerometerUpdates();
        NSOperationQueue.currentQueue().cancelAllOperations();
    }
      
    function tapReset() {
        chart.removeAllData();
        while(dataPoints.length > 0)
        {
            dataPoints.pop();
        }
      
        buildChartWithPoints(0);
    }
      
    exports.tapStart = tapStart;
    exports.tapStop = tapStop;
    exports.tapReset = tapReset;
  7. Finally, we can add some CSS to style the buttons. To do so, open the app.css file and add the following CSS:

    button {
        font-size: 20;
        background-color: gray;
        color: #3BB9FF;
        margin: 10;
    }
With this the coding part is done. It’s time for testing.

Testing the App

If we were about to write an app which is not dependent on any sensors, we would have probably tested it in a simulator using the following command:

tns run ios --emulator

But, since we are relying on the accelerometer data, in order to get a working app, we should deploy the app to the device using the following command:

tns run ios

This is it! You can find the complete Seismograph project at GitHub.

Note that in order to run this repo, you need to add the Telerik UI for iOS library following the instructions discussed earlier in the Adding the native library section of this article.

So this is how you can create a native iOS app using the NativeScript framework and a 3rd-party library. As I mentioned, Telerik AppBuilder clients will soon be able to load 3rd-party frameworks and plugins, but that’s not all - a common API wrapper will be built around the Telerik UI for iOS and UI for Android suites, and thanks to it you will be able to use the Telerik iOS and Android components from a single shared JavaScript code base to build cross-platform apps.

What about cross-platform wrappers for your favourite iOS and Android libraries?

We are about to release a NativeScript plugins marketplace in the end of June which will offer cross-platform components for your NativeScript development. These will be native iOS and Android libraries wrapped by a common API JavaScript wrapper. Let us know if you are the author of iOS/Android libraries that you would like to see their NativeScript wrappers listed in the marketplace, or if your are stepping into NativeScript and are missing some of your favourite native libraries in their NativeScript representation. 

Happy coding!