How to get a persistent unique device ID in Ionic for iOS

Simple way to store uuid as device.uuid. Using the Native cordova device plugin gives inconsistent results in iOS. Not only this it changes every time the app is installed. Might be a rare case for some but in the app i’m building we use it to lockdown which devices can use the app irrespective of login credentials.

I used this in conjunction with the hashids library. And you will need to follow the instructions to include the KeyChain plugin and ionic library.

validateUuid() {
    let date = new Date();
    let components = [
        date.getFullYear(),
        date.getMonth(),
        date.getDate(),
        date.getHours(),
        date.getMinutes(),
        date.getSeconds(),
        date.getMilliseconds()
    ];

    let id = components.join("");
    let hashids = new Hashids();
    let newKey = hashids.encode(id).toUpperCase();

        return new Promise((resolve,reject) => {
            if(window.cordova) {
                this.keychain.get('APP_NAMESPACE_KEY').then((uuid) => {
                    if (!uuid) {
                          this.keychain.set('APP_NAMESPACE_KEY', newKey, false).then(() => {
                              this.appService.set('deviceId',newKey);
                              return resolve();
                          }).catch((err) => {
                              return resolve();
                          });

                      } else {
                          this.appService.set('deviceId', val);
                          return resolve();
                      }
                }).catch(() => {
                    //something went wrong
                    return resolve();

                });
            } else {
                this.appService.set('deviceId','browser-id');
                return resolve();
            }
        }).then(() => {
            if(this.platform.is('android')) {
                this.appService.set('deviceId',window.device.uuid);
            }
        });
}

Here our validateUuid function returns a promise, first we generate a new hash in case we need it later, then first check if the keychain value exists (APP_NAMESPACE_KEY). If it does return that value, or create it.

We also do a check if running in the browser by checking for window.cordova or not, if not returning a default “browser-id”.

In my app I have an appService that sets/gets the value of the deviceId which is used elsewhere around the app.

Finally if running on Android then simply set it to window.device.uuid as Android does not currently change this each time.

 

Living in Angular Ionic RC land and the state of Frontend

For the past few months i’ve been upgrading an Ionic v1 app to v2, watching at the sidelines as a new bread of web applications take form.

As a developer trying to use these platforms it’s been incredibly frustrating, but living on the very edge does have it’s perks, bringing a deeper understanding of the frameworks, one I wouldn’t have gained from simply installing a stable release.

A friend also recently pointed me to the amusing article on hackernoon https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f#.xv563r3w7

Sadly most of it is true.

Touching on the above frameworks and other libraries, the main annoyance is that many come with build tools, great! but little guidance on integrating other tasks or extending the tools. Other libraries suffer this as well where the developer made a simple build script but doesn’t explain how to use it if you’ve got an existing project, or your simply forced into using their way of doing things.

Finally i’m near the end of the proposed development with my Ionic project, though Ionic2 still has some bugs, and a fairly decent understanding of Angular 2 workings only to find that in the coming months a major release of Angular4 will be released!

Hopefully not too many changes to come in that release.

 

Migrating Angular1.x to 2 with TypeScript

Whichever release candidate your using, or the final release. This a quick heads up guide on how to port over some simple tasks, something the Hero example app doesn’t show in full.

Upgrade path

I tried using the upgrader but in reality if your strapped for time I think using that is a very lengthy time consuming process and only appeals to projects that are running continuous delivery (that’s if you can get it set up in the first place).

As your adding the overhead of creating the upgrade/dowgrade code and then after that having to refactor.

I opted for a re-build of the current app refactoring service by service etc, it also allows you to make some architectural changes to the api of your app along the way, how much dependant on the time you have.

The biggest challenges are where the previous app uses $q, $watch and where you’ve used angular’s built in methods like angular.extend some can be fairly quickly swapped for lodash alternatives though or another framework, but overall it’s the sheer amount of typing you will need to do that gets tedious especially for Typescript.

Loading config

In Angular 1 you might have had some app wide config going on that sat in the main app.js e.g

angular.module('myApp')

    .config(function (appConfig) {
//configure stuff here

And also through a constant

angular.module('myApp').constant('appConfig', {
    "version": "1.0"
})

In Angular2 there’s two ways to solve this problem one is to provide the value on the root level app component like so:

providers: [
        { provide: VERSION, useValue: '1.0' }
    ],

You can then get that value by doing

var version = injectorValue.get(VERSION);

That’s great for small one liner configurations but a better way would be to load it from a local file. We can do that with a simple service.

import { Injectable } from '@angular/core';
var config = require('./env.json');

@Injectable()
export class AppConfigService {

    private _config: Object;

    constructor() {
        this._config = config;
    }

    get(key: any) {
        return this._config[key];
    };
    getAll() {
        return this._config;
    }
}

In this case the env.json is generated externally from a gulp task. If you wanted to load an external file over http you could do something like below, here we would need to provide the endPoint value as we did above to the main app component.

providers: [
        { provide: endPoint, useValue: 'http://127.0.0.1/api' }
    ],
import {Http} from '@angular/http';
import {Injectable, Inject} from '@angular/core';

@Injectable()
export class AppConfig {

    constructor(private http: Http, 
                @Inject('AppConfig') private endPoint: string) { }

    getConfig(): {
        return this.http.get(`${this.endPoint}/`)
            .then((response) => {
                //do something with the response
            });
    } 
    // the rest of the code...
}

2. Promises

I won’t go into too much detail on this as there are some excellent guides around but where your code uses $q in Angular1 you will need to convert that to using an ES6 promise.

var promise = $q.when([]);
promise = promise.then(function() {
                        return getSomeData();
                    });
promise = promise.then(function() {
// do something else
});

In ES6 we could convert it like this

return new Promise((resolve,reject) => {
    getSomeData().then((result) => {
       //do something with result
       resolve(result);
}).catch((err) => {
       reject(err);
}
)};

or chain them together

let promise = new Promise((resolve,reject) => {
    getSomeData().then((result) => {
       //do something with result
       resolve(result);
}).catch((err) => {
       reject(err);
}
)};
promise.then((res) => {
   //do another thing
});

3. using $this _self etc

In most cases you should avoid doing that anyways either using .bind(this) although there is the odd case you need to, but with ES6 it makes it much easier to avoid through using arrows. In simple terms it’s shorthand for a function() {}, but the added feature of keeping the context of this to the parent. E.g

var $this = this;
Catalogue.getChildren(catId)
            .then(function(categories) {
                availableFilters.subCategories = categories;
            }).then(function(categories) {
                return CatalogueRepository.getFilters($this.getCriteria());
            })

Using arrows we no longer need $this

Catalogue.getChildren(catId)
            .then((categories) => {
                availableFilters.subCategories = categories;
            }).then(function(categories) => {
                return CatalogueRepository.getFilters(this.getCriteria());
            });

4. Angular forEach,copy etc..

for(var data in key) {
   key //key
   data[key] //value
}

Whilst there is vanilla js alternatives for these the quickest solution is to just import lodash or other library, lodash has _.deepClone _.forEach I use it like so after the imports.

var _ = require('lodash');

5. Window

For RC4 Provide window in your root component

providers: [
        { provide: Window, useValue: window }
    ],

In your component constructor use

constructor(
     @Inject(Window) private _window: Window
)

There may be a better way of doing the above, please let me know if you do it a different way.

6. Using pipe inside a service

Import the pipe and then call it like below.

import {TranslatePipe} from "../pipes/translate.pipe";
constructor(private translate: TranslatePipe) {
}
myFunction() {
this.translate.transform('some text');
}

7. Global variables

If for example your using another framework or something else in the global namespace you can use this to avoid TS errors, e.g using Ionic with Cordova.

declare var cordova: any;

@Injectable()
export class MyService {}

8. Code organisation

The sheer amount of imports can become a bit bewildering, to overcome this use barrel files, you have a bunch of services in /services then create a file inside there called index.ts

Inside this put:

export {appService} from "./appService";
export {configService} from "./configService";
export {dbService} from "./dbService";
export {UserDb} from "./userDb";

You can import it into your components like so:

import * as coreServices from "./services/";

You can provide or bootstrap them using [coreServices.UserDb,coreServices.dbService] etc..

One disadvantage to this is that you can’t quickly see the list of services upfront, so it’s best to group them for example /db /file /network and have the corresponding index files.

import * as network from "./network/";
import * as db from "./db/";
import * as files from "./file/";

This is much more readable.

9. Data in templates

If you need to load some data before it’s available to the template in Angular2 and want to avoid the undefined errors, there are a few ways of solving this.

1. ngOnInit( this.state = getMyState(); );
2. “The Elvis Operator” before your data e.g {{state?.name}}
3 ngIf e.g *ngIf=”state”
4. construct your model which will give you an empty object. this.state = new myModel();

10. Global services

In angular1 every service was a singleton, v2 has the same concept if you inject the service at the app level, but you can also provide it separately to to individual areas of the app through the providers:[], read more about that here

That’s it for now, let me know if you have any other simple things.