abhayastudios / nativescript-contacts-lite

This nativescript-contacts-lite plugin provides pretty fast read-only access to the iOS and Android contact directory.
MIT License
13 stars 7 forks source link

NativeScript Contacts Lite

This nativescript-contacts-lite plugin provides pretty fast (but hey it's all relative) read-only access to the iOS and Android contact directory. By limiting the scope of the result set through the desiredFields, it is possible to obtain a JSON object containing the relevant data of the contact directory within a couple of hundred milliseconds.

Demo Application

This repository contains a demo application in the demo-angular folder that uses this plugin to display a contact picker. The demo app can be a good starting point for your app and may be used for narrowing down issues whilst debugging. Just clone this repo and run tns run <platform> in the demo-angular folder.

Installation

Run tns plugin add nativescript-contacts-lite

Usage

To use the contacts module you must first require() it.

var Contacts = require("nativescript-contacts-lite");

Methods

getContacts & getContactsWorker

Both methods retrieve contacts and share the same interface. The difference is that the former runs in the main thread and the latter within a web worker.

Argument 1: desiredFields An array containing the desired fields to fetch from phone's storage backend. Possible values are:

[
  'address',
  'display_name',
  'email',
  'name_details',
  'nickname',
  'note',
  'organization',
  'phone',
  'photo',
  'thumbnail',
  'website'
]

Argument 2: searchTerm (optional) A string with a search term to limit the result set to only contacts whose display_name contain the term. Defaults to fetching all relevant contacts if an empty search term is provided or none at all.

Argument 3: debug (optional) Boolean (true/false) determining whether to pass debug messages to the console. Defaults to false.

Example using getContacts

let desiredFields = ['display_name','phone'];
let searchTerm = 'Jon';

console.log('Loading contacts...');
let timer = new Date().getTime();

Contacts.getContacts(desiredFields,searchTerm).then((result) => {
  console.log(`Loading contacts completed in ${(new Date().getTime() - timer)} ms.`);
  console.log(`Found ${result.length} contacts.`);
  console.dir(result);
}, (e) => { console.dir(e); });

Example using getContactsWorker

let desiredFields = ['display_name','phone','thumbnail','email','organization'];

console.log('Loading contacts...');
let timer = new Date().getTime();

Contacts.getContactsWorker(desiredFields).then((result) => {
  console.log(`Loading contacts completed in ${(new Date().getTime() - timer)} ms.`);
  console.log(`Found ${result.length} contacts.`);
  console.dir(result);
}, (e) => { console.dir(e); });

getContactById

Get contact details for a specific contact.

Argument 1: contactId The identifier of the contact you wish to obtain details of (obtained through the getContacts(Worker) methods).

Argument 2: desiredFields An array containing the desired fields to fetch from phone's storage backend. See getContacts method for possible values.

Argument 3: debug (optional) Boolean (true/false) determining whether to pass debug messages to the console. Defaults to false.

Example

let contact_id = contact.contact_id // get id from result of getContacts method

let desiredFields = [
  'address',
  'display_name',
  'email',
  'name_details',
  'nickname',
  'note',
  'organization',
  'phone',
  'photo',
  'thumbnail',
  'website'
]

Contacts.getContactById(contact_id,desiredFields).then((result) => {
  console.dir(result);
}, (e) => { console.dir(e); });

Performance

Considerations

Running in main thread versus web worker

The plugin provides both methods that run in either the main/UI thread or within a web worker. Although offloading the processing to a separate thread adds web worker initialization time, it guarantees that the main UI thread will continue to work smoothly.

If you are implementing an autocomplete where on each key you are querying a smaller subset of the contacts, you will probably want to go with the non-worker variant to avoid web worker initialization time while the user is waiting. On the other hand, if you are reading the entire contact directory while initializing your app, you probably want this to happen in the background to avoid the UI getting stuck while processing. In the latter case you probably would want to use the web worker variant.

Contact Picker Example

Another way to speed up performance is possible in certain cases like when you are building a contact picker. In this case it is probably good enough to first provide a narrow array of desiredFields like ['display_name','thumbnail'] to getContacts to display the list. Only when the user selects a specific contact, you can obtain all details for a specific contact by supplying a wider array of desiredFields to getContactById. This example has been implemented in the demo app located in this repository.

Benchmarks

Android

On a relatively old Samsung Galaxy S4 a list of ~600 contacts is returned somewhere between ~300ms up to ~2s depending on the desired fields and whether you run in the main thread or in a web worker.

iOS

Tests on an iPhone 7 plus with ~600 contacts returned in ~105ms when running getContacts(['display_name', 'phone']) (so non worker). This could use some more real iOS device data in different modes (e.g. more fields & web worker mode) if anyone has some.

Notes

Bundling with Webpack

This plugin is compatible with webpack bundling through the nativescript-dev-webpack plugin as of NativeScript 3.2. However, if you are using the web worker functions, we need to ensure that the web worker resources are included in the package. For this purpose, you should add the nativescript-worker-loader to your project: npm i -D nativescript-worker-loader.

Photo & Thumbnail Images

The plugin returns photo & thumbnail images as a base64 encoded string ready to be used as the source attribute of an image, e.g. <Image *ngIf="item.thumbnail" [src]="item.thumbnail"></Image>

Android Specifics

Permissions

This plugin uses the nativescript-permissions plugin by Nathanael Anderson for obtaining read-only permissions to the phone's contacts on Android 6 and above.

iOS Specifics

Since the plugin uses the Contact framework it is supported only on iOS 9.0 and above!

Permissions

As of iOS 10 it has become mandatory to add the NSContactsUsageDescription key to your application's Info.plist (see Apple's developer documentation).

Therefore you should add something like this to your ~/app/App_Resources/iOS/Info.plist:

<key>NSContactsUsageDescription</key>
<string>This application requires access to your contacts to function properly.</string>

Acknowledgements

The iOS part of this plugin is based on code from the nativescript-contacts plugin.