NativeScript iOS Push Notifications & How to Get Your Device Token When Testing & Debugging in Xcode 8

I found all the articles and frameworks and this still took me quite some time to figure out.  As far as I can tell this is easiest to do using Xcode 8.  Some preliminary items you’ve probably already taken care of if you’ve found yourself here:

Ok, you should be ready to get busy.

Adding The Push Plugin Code To Your Project

From what I can tell it’s not so important where this code is added but seems ideal to put it in the main page js file, the first that loads.  You need to use an observable and bind it to your page.  I had an app that was well into version 2 that I wanted to add push notifications to.  It was setup as many examples are from a year or more ago using

exports.loaded = function(args) { 
  main code here that is run when the first page is loaded  
}

All the examples online use a main-page-view.js observable that’s instantiated in the other main js file and it’s all very complex.  What it boils down to is you need to reference the main observable that’s being bound to your page in the var self = xxx line of the push notifications example code.  In its shortest form you would do something like this:

https://gist.github.com/ChrisFlannagan/8ce46cfd02ab32a291bbeb026624f2a7

What’s super import here is the var self = pageData.  We are putting our observable into a variable called “self” that is used in the push notifications code from the readme.

I was just copying the code into my exports.loaded function and kept getting an error like self.set is not a function and was getting really irritated.  So do what I did above or start a fresh project and copy from the sample push plugin repo here.

Getting Your Device Token

In order to test notifications you need to run your app on an actual device.  So plugin your iPhone or iPad and get ready to run your app on it through either Xcode or tns command line.  I always get confused on how to select the right device on command line so I just use Xcode.

You will also need to make sure you have all the example code in place for registering your device.  I’ve built something you can copy here, just make sure that exports.loaded is initiated on the first page load.  The main xml would look like this in the top tag:

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="loaded">

I’ll also embed the full javascript sample code at the bottom of this post so you don’t have to click out of here.

Now you need to get your device token from your iphone/ipad which you will need to use in an app like Easy APNs Provider in order to send test notifications to your phone.  I could not find any functionality in the nativescript plugin but on the link I shared earlier the fella shows how to do it in swift.  These lines here:

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
    print("DEVICE TOKEN = \(deviceToken)")
}

You can see the long ass function name there.  I found that function in the nativescript plugin files in node_modules.  So you need to temporarily edit (I know, I know) the file and add a console.log to it.  In node_modules/nativescript-push-notifications/push-plugin.ios.js on, at least in the current version, line 41 you will see

var token = result.userInfo.objectForKey('message');

Under that add a console log:

var token = result.userInfo.objectForKey('message');
console.log(token);

Now when you run the app and accept the push notifications alert it will log in your terminal the device token.  DO NOT FORGET to edit that console log out.  Gotta keep them node_modules pure ya know.

Testing It Out

If you did everything right you can use the app I linked to earlier, Easy APNs Provider, and run some tests.  It worked flawlessly for me once I got the code running.  Good luck! Hit me up if you get stuck @ChrisFlanny.  Here’s a full sample code for doing a simple implementation inside an exported function:

https://gist.github.com/ChrisFlannagan/7f352ec635f1e4dc193ed62e4128609c

Touch “Hold” Function: Button Held Down In NativeScript

Tonight I needed to run a function constantly while a button is being held down in my app.  I found the gestures functionality and it had what I needed but I had to do some work to get it to perform what I was after.

My task was this, when someone puts their finger down on a ‘Record’ button I need to store what the sound level was every 10/1000 of a second.  So here’s what I did.

Using Gestures Touch to Define “Hold”

When the page is loaded I assign the button element to a variable then I add the on touch function.  It’s important to declare that button’s variable at the beginning of your JS so it’s available in all functions.  Also important to require your view module and gesture.  Here’s a snippet that covers all of that:

var viewModule = require("ui/core/view");
var audio = require("nativescript-audio");
var timer = require("timer");
var gestures = require("ui/gestures");
var documents = fs.knownFolders.currentApp();
var recorder = new audio.TNSRecorder();
var isRec = false;
var timeRec;
var currentRecording = new Array();
var page;
var soundlevel;

exports.loaded = function(args) {
    page = args.object;
    soundlevel = viewModule.getViewById(page, "soundlevel");

    soundlevel.on(gestures.GestureTypes.touch, function (args) {
        if(args.action == "down" || args.action == "up") {
            recordVolume( args.action );
        }
    });
}

Now that I’m calling a function, let’s tell that function what to do.  We have our “soundlevel” view which is just a button with id=”soundlevel” and we’ve assigned an action of “on” for it when gesture touch occurs.  The gesture touch passes args that include “action” as a property.  That property can be “down”, “up” amongst other things, but these are what we need.  So now let’s build a function to handle the hold.

function recordVolume(action) {
    var recorderOptions = {
        filename: documents.path + "/recording.caf",
        infoCallback: function() {
            console.log();
        },

        errorCallback: function() {
            console.log();
        }
    };
    if( action == "up" ) {
        recorder.stop();
        isRec = false;
        timer.clearInterval(timeRec);
    } else {
        recorder.start( recorderOptions );
        currentRecording = new Array();
        isRec = true;
        timeRec = timer.setInterval( function() {
            var barWidth = Math.round((160+Number(recorder.getMeters(0, "average")))/160*100);
            currentRecording.push(barWidth);
        }, 10);
    }
}

Note: barWidth is an integer I end up assigning as a percentage of the screen width.  This way I can give a visualization of sound level.

Here we check wether the action is up or down and do our desired functionality based on that.  If it’s “up” that means the finger has been lifted from the screen so we stop everything.

If the action is “down” that means the user has just pressed the button.  So we user the timer library and set an interval.  Basically every 10/1000 of a second we perform a calculation of the sound level and push that to our array of sound levels.  Now we can analyze that and do what we want to do!

 

Don’t Touch Things In /platform/

I learned this the hard way just like every thing else in here related to NativeScript.  It’s a young framework and there is not a ton of documentation or much of a community yet.  But I love it and the slack community is pretty active.

I finally finished an app and needed to get it in iTunes for my boss.  I started following build instructions using Xcode which involved following along with NativeScript’s docs and also getting linked over to Apple’s dev docs a lot.  Somewhere along the way I felt that I needed to make some changes to my app in Xcode so that it would be able to get submitted for review as it kept hitting errors when trying to upload.

Icons

Icons are handled in a way that’s not overly complex but super easy to screw up.  When you open your project that NativeScript generates you will see a lot of stuff going on.  One of those is the “Use Asset Catalog” option for your icons.

If you have any issues with icons when trying to upload to iTunes you will probably start toying with the Asset Catalog and your info.plist.  One word of advice: DON’T!

NativeScript handles this for you just fine.  Make sure you have all required icons in your /app/App_Resources/iOS directory (including your 120px ones! I.e. 40@3x and so on, these are new and screwed me up bad).

tns prepare ios

The point is, when you want to build your app just run tns prepare ios then open the project in Xcode.  You can have it handle some of the automated tasks you might need when it pops up its warnings, but don’t screw with the .plist file or the asset catalog stuff or you will spend an entire day like me trying to figure out what’s wrong.

When you run the prepare CLI command NativeScript builds out a fully functional Xcode project for you.  Other than dealing with setting up your Apple developer account and connecting it to Xcode there’s not much else for you to do.  When you are ready to build and submit to the app store all you have to do is make sure the device selected is “Generic iOS Device” hit Product > Archive then when the Organizer window opens Validate then Upload to iTunes.