Back to Blog Home
← all posts

Empower your Capacitor developments with NativeScript

June 9, 2021 — by Technical Steering Committee (TSC)

Let's say you have a web app and you want to deploy it to mobile app stores today. You can use Capacitor to do it. If you've used Ionic to build your UI you are likely in an even better position given both are made by the same team and work really well together.

What also works well with both is NativeScript, giving you more options to solve your mobile development challenges.

In February of 2021, we announced a public beta of NativeScript for Capacitor and we greatly appreciate all the feedback we received. It allowed us to fix a few key details and improve how you can use it to solve your own challenges.

NativeScript for Capacitor 1.0

With the recent announcement of Capacitor 3, we are releasing NativeScript for Capacitor 1.0 and having a lot of fun with it already.

It's not uncommon for development teams to be split up (or departmentalized) by their language skills creating silo's of communication, understanding and the need to write more documentation that teams need to maintain and understand. This adds another layer to your development stack that you or someone on your team will one day need to debug, evolve and/or simply update. For example, a platform API team (Objective C, Swift, Java, Kotlin, etc.) will often create a wrapper/plugin around a specific set of platform API features and expose a new API which is then given to web developers to use for those exact platform needs.

NativeScript allows native platform developers to work hand in hand with web development teams in the same code base and even in the same IDE. In our experience we find web developers become even better native platform developers by using JavaScript, often their language of choice, with their NativeScript abilities to use platform source documentation to solve real day to day challenges.

  • If you're a web developer, you now have the power to reach out and touch your native device platform API directly.
  • If you're a native platform API developer, you now have the ability to deliver solutions that speak the web development teams language of choice, JavaScript.

This allows both departments to solve day to day challenges together; increasing team understanding and communication around all APIs that affect the project development.

To use in your Ionic + Capacitor developments today, it's all rolled up into one convenient npm package:

yarn add @nativescript/capacitor

Or if using npm : npm install @nativescript/capacitor

See more in the docs here

Some examples

Let's look at an Android example of programmatically setting the device screen brightness that you can write in your Capacitor app which may be utilizing Ionic with Angular, React, or Vue right now with NativeScript:

// change Android screen brightness
const context = native.androidCapacitorActivity;
if (!android.provider.Settings.System.canWrite(context)) {
  const intent = new android.content.Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
  intent.setData(android.net.Uri.parse("package:" + context.getPackageName()));
  intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
  context.startActivity(intent);
}

if (android.provider.Settings.System.canWrite(context)) {
  android.provider.Settings.System.putInt(context.getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS, value * 100);
}

🚨 New Quality Assurance Bug Ticket

We are receiving reports of crashes on older Android devices when users navigate into the "movie mode" feature in the app where we should expect to observe the screen brightness auto increased to at least 80%.

Oh wait, you mean Android API Level 22 and below use a different API to set the screen brightness?!

Ordinarily this surprise would invoke the need for a platform API developer to make a change to a plugin which would then need to be rebuilt, version bumped and then republished for the web development team to get the modification.

We prefer just being able to do this:

// change Android screen brightness (including API level 22 and below)
const context = native.androidCapacitorActivity;
if (android.os.Build.VERSION.SDK_INT < 23) {
  const attr = context.getWindow().getAttributes();
  attr.screenBrightness = value;
  context.getWindow().setAttributes(attr);
} else {
  if (!android.provider.Settings.System.canWrite(context)) {
    const intent = new android.content.Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(android.net.Uri.parse("package:" + context.getPackageName()));
    intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
  }

  if (android.provider.Settings.System.canWrite(context)) {
    android.provider.Settings.System.putInt(context.getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS, value * 100);
  }
}

Done and moving on.

NativeScript has been written with TypeScript from the very beginning (circa 2014) and thus every API you handle in NativeScript is strongly typed against definitions which are mapped out directly from the platform API. The typings are made available via @nativescript/types. What each platform marks as a public available API is intended for you to develop with and thus mapped into strongly typed constructs you can use in your TypeScript codebase.

Let's take one last look at an iOS example for doing the same thing, setting screen brightness:

UIScreen.mainScreen.brightness = .8;

That's it? You mean I don't need a plugin to do that?

Well one, @nativescript/capacitor, which is like getting hundreds of plugins for one.

Using platform APIs from your web components

By simply importing the native object from @nativescript/capacitor you have a whole new angle of solving real world development challenges without even leaving your web component:

import { native } from '@nativescript/capacitor';

native.UIScreen.mainScreen.brightness.set(.8);

Huh?

The native object is a JavaScript Proxy that's only active when executed from the context of the platform device runtime, giving you the utmost delightful flexibility to access all those platform APIs quickly without even leaving your web components. This means when the native object is executed in a standalone web browser it's simply inactive and has no effect on web browser deployments.

This is powerful. However we encourage something even better...

Creating a platform API helper for nice project organization

Most JavaScript developers are used to creating pure methods for nice compositional development growth. We encourage the same with NativeScript.

Perhaps even easier is to define your own native helper methods for any number of platform API behavior you need to handle.

For example let's do that for our screen brightness above:

/**
 * Adjust iOS and Android screen brightness
 */
native.setScreenBrightness = (value: number) => {
  if (native.isAndroid) {
    const context = native.androidCapacitorActivity;
    if (android.os.Build.VERSION.SDK_INT < 23) {
      const attr = context.getWindow().getAttributes();
      attr.screenBrightness = value;
      context.getWindow().setAttributes(attr);
    } else {
      if (!android.provider.Settings.System.canWrite(context)) {
        const intent = new android.content.Intent(
          android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS
        );
        intent.setData(
          android.net.Uri.parse("package:" + context.getPackageName())
        );
        intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
      }

      if (android.provider.Settings.System.canWrite(context)) {
        android.provider.Settings.System.putInt(
          context.getContentResolver(),
          android.provider.Settings.System.SCREEN_BRIGHTNESS,
          value * 100
        );
      }
    }
  } else {
    UIScreen.mainScreen.brightness = value;
  }
};

Then just use in your Ionic component anytime you need to adjust the mobile device screen brightness programmatically:

native.setScreenBrightness(.8);

Wait just a minute there, isn't this creating a new API for the web development team to learn?

One that can be commented, auto annotated through TypeScript import intellisense, maintained, modified, refactored, reintegrated into your web app and clearly understood without leaving your own codebase. Then yes certainly. However one that native platform API developers and web developers have control over at a moments notice with no further delay and it's strongly type checked allowing easy refactors and improvements over time.

The strong typing of custom native helper methods you create is what the native-custom.d.ts is for which you'll find the plugin adds to your codebase, for example:

  • native-custom.d.ts:
declare module '@nativescript/capacitor' {
  export interface customNativeAPI extends nativeCustom {}
}

/**
 * Define your own custom strongly typed native helpers here.
 */
export interface nativeCustom {
  openNativeModalView: () => void;

  // For the example above we can just add strong typing for it here:
  setScreenBrightness: (value: number) => void;
}

The plugin comes with 1 simple example to give you guidance on doing such a thing. You can augment as many custom native helper methods there as your project needs.

OpenJS Presentation and Other Video Learning

You can watch an entertaining presentation from OpenJS World 2021:

https://www.youtube.com/watch?v=OInjFvniccU

You can also watch Episode BLS035 provided by beeman.dev:

https://www.youtube.com/watch?v=AHcL_UbnnPY

Help shape the future

NativeScript graduated with OpenJS Foundation and we are committed to a transparent open source future. You can contribute in a number of helpful ways:

Celebrate the wonders and joys of JavaScript development with us anytime.