Hey guys, In this article I want to show you how I was able to read and filter mobile contacts in react-native. There is no direct guide available so far. So I thought of writing one here.
To read contacts, I have used react-native-contacts library from github. However, this library does not allow me to read contacts directly because of the permissions. We need to get permissions from the platform to read contacts.
I have used react-native-permissions library for getting necessary permissions to read contacts. You may have to read installation guides from both repositories to use them into your project.
So let’s not waste time and code..
Let’s Open/Create a react-native project, I assume you are familiar with react-native and skipping the project creation and running.
Once the minimum functionality app is running in your mobile, you can install both react-native permissions and react-native-contacts by following their installation instructions. In short, you will have to install them by running below commands and follow their extra steps for IOS and Android separately.
npm install react-native-contacts --save
npm install --save react-native-permissions
NOTE: The above commands are only short version of installation process, you will have to install them by following the instructions from their repositories.
Now let’s create a component in your project and name it as Contacts.js. The Contacts.js component looks like this,
import React, {Component} from 'react'
import {View} from 'react-native'
import {Text} from 'react-native-elements'
export default class Contacts extends Component {
constructor (props) {
super(props)
}
componentDidMount () {
console.log('Component Mounted')
}
render () {
return (
<View>
<Text>READING CONTACTS</Text>
</View>
)
}
}
Let’s add react-native-permissions import to the above file.
import {request, PERMISSIONS, RESULTS} from 'react-native-permissions'
Here is how we use react-native-permissions..
request(PERMISSIONS.ANDROID.READ_CONTACTS).then(result => {
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
'This feature is not available (on this device / in this context)',
)
break
case RESULTS.DENIED:
console.log(
'The permission has not been requested / is denied but request able',
)
break
case RESULTS.GRANTED:
console.log('The permission is granted')
break
case RESULTS.BLOCKED:
console.log('The permission is denied and not request able anymore')
break
}
})
As you can see, we have used PERMISSIONS to tell what permissions we need, in this case it is reading contact permission from android. You can read more permissions from the github repository. We also use RESULTS to identify if the permission is granted or not.
Lastly, we also have used request to request permissions using PERMISSIONS and RESULTS.
So let’s put above react-native permissions code in our componentDidMount method. And check if we get a popup asking permissions for reading contacts.
import React, {Component} from 'react'
import {View} from 'react-native'
import {Text} from 'react-native-elements'
import {request, PERMISSIONS, RESULTS} from 'react-native-permissions'
export default class Contacts extends Component {
constructor (props) {
super(props)
}
componentDidMount () {
request(PERMISSIONS.ANDROID.READ_CONTACTS).then(result => {
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
'This feature is not available (on this device / in this context)',
)
break
case RESULTS.DENIED:
console.log(
'The permission has not been requested / is denied but request able',
)
break
case RESULTS.GRANTED:
console.log('The permission is granted')
break
case RESULTS.BLOCKED:
console.log('The permission is denied and not request able anymore')
break
}
})
}
render () {
return (
<View>
<Text>READING CONTACTS</Text>
</View>
)
}
}
If you run your application, you will get a popup asking permission for accessing contacts. Depending upon your reaction one of the four switch RESULTS cases gets called.
Now let’s add the code for reading contacts, below is how you import react-native-contacts.
import Contacts from 'react-native-contacts'
And this is how you use react-native-contacts.
Contacts.getAll((err, contacts) => {
if (err === 'denied') {
// error
} else {
// read contacts here...
}
})
Let’s add above code to RESULTS.GRANTED case in componentDidMount method.
import React, {Component} from 'react'
import {View} from 'react-native'
import {Text} from 'react-native-elements'
import {request, PERMISSIONS, RESULTS} from 'react-native-permissions'
import Contacts from 'react-native-contacts'
export default class Contacts extends Component {
constructor (props) {
super(props)
}
componentDidMount () {
request(PERMISSIONS.ANDROID.READ_CONTACTS).then(result => {
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
'This feature is not available (on this device / in this context)',
)
break
case RESULTS.DENIED:
console.log(
'The permission has not been requested / is denied but request able',
)
break
case RESULTS.GRANTED:
console.log('The permission is granted')
Contacts.getAll((err, contacts) => {
if (err === 'denied') {
// error
} else {
// read contacts here...
console.log(contacts)
}
})
break
case RESULTS.BLOCKED:
console.log('The permission is denied and not request able anymore')
break
}
})
}
render () {
return (
<View>
<Text>READING CONTACTS</Text>
</View>
)
}
}
If you run the app and give permissions you should be able to see contacts in the log. Each contact record will have lot of details, you can check them here, but I just need fullName, phoneNumber, email, hasThumbnail and thumbnailPath.
There are also some contacts with more than one phone number and email. We need to create a filter that does not duplicate results and identify each phone number and email as unique contact record.
Before we filter contacts, We also need to format each phone number, to do this job let’s install libphonenumber-js from here. And this is how we import the libphonenumber-js package.
import {parsePhoneNumberFromString, ParseError} from 'libphonenumber-js'
And this is how we use to format a phone number.
function formatNumber (num) {
try {
const formatted = parsePhoneNumberFromString(num, 'IN')
if (formatted.isValid()) {
return formatted.number
} else {
return ''
}
} catch (error) {
if (error instanceof ParseError) {
return ''
}
return ''
}
}
If the number does not have a country code, the above code will consider it as Indian number and add +91, you can change it to your country if you want by changing ‘IN’ to your country identifier.
Now let’s filter the contacts by using below function that I have prepared for myself.
function contactsFilter (contacts) {
if (contacts != null && contacts.length > 0) {
let cts = []
contacts.filter(contact => {
if (contact.phoneNumbers != null && contact.phoneNumbers.length > 0) {
let n1 = ''
contact.phoneNumbers.filter(number => {
let num = formatNumber(number.number)
if (
contact.emailAddresses != null &&
contact.emailAddresses.length > 0
) {
contact.emailAddresses.filter(email => {
if (n1 != num) {
n1 = num
if (email != null && email.email != null) {
let cObj = {}
cObj['fullName'] =
contact.givenName +
' ' +
(contact.givenName != contact.familyName
? contact.familyName
: '')
cObj['email'] = email ? (email.email ? email.email : '') : ''
cObj['phoneNumber'] = num
cObj['hasThumbnail'] = contact.hasThumbnail
cObj['thumbnailPath'] = contact.thumbnailPath
cts.push(cObj)
}
}
})
} else {
if (n1 != num) {
n1 = num
let cObj = {}
cObj['fullName'] =
contact.givenName +
' ' +
(contact.givenName != contact.familyName
? contact.familyName
: '')
cObj['email'] = ''
cObj['phoneNumber'] = num
cObj['hasThumbnail'] = contact.hasThumbnail
cObj['thumbnailPath'] = contact.thumbnailPath
cts.push(cObj)
}
}
})
} else {
if (
contact.emailAddresses != null &&
contact.emailAddresses.length > 0
) {
contact.emailAddresses.filter(email => {
let cObj = {}
cObj['fullName'] =
contact.givenName +
' ' +
(contact.givenName != contact.familyName
? contact.familyName
: '')
cObj['phoneNumber'] = ''
cObj['email'] = email ? (email.email ? email.email : '') : ''
cObj['hasThumbnail'] = contact.hasThumbnail
cObj['thumbnailPath'] = contact.thumbnailPath
cts.push(cObj)
})
}
}
})
return cts
}
return null
}
The above code will return Array containing each contact record object with fullName, phoneNumber, email, hasThumbnail and thumbnailPath as keys. Feel free to change the above code for your use case.
Lets add everything together.
import React, { Component } from 'react'
import { View } from 'react-native'
import { Text } from 'react-native-elements'
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions'
import Contacts from 'react-native-contacts'
import { parsePhoneNumberFromString, ParseError } from 'libphonenumber-js'
function formatNumber (num) {
try {
const formatted = parsePhoneNumberFromString(num, 'IN')
if (formatted.isValid()) {
return formatted.number
} else {
return ''
}
} catch (error) {
if (error instanceof ParseError) {
return ''
}
return ''
}
}
function contactsFilter (contacts) {
if (contacts != null && contacts.length > 0) {
let cts = []
contacts.filter(contact => {
if (contact.phoneNumbers != null && contact.phoneNumbers.length > 0) {
let n1 = ''
contact.phoneNumbers.filter(number => {
let num = formatNumber(number.number)
if (
contact.emailAddresses != null &&
contact.emailAddresses.length > 0
) {
contact.emailAddresses.filter(email => {
if (n1 != num) {
n1 = num
if (email != null && email.email != null) {
let cObj = {}
cObj['fullName'] =
contact.givenName +
' ' +
(contact.givenName != contact.familyName
? contact.familyName
: '')
cObj['email'] = email ? (email.email ? email.email : '') : ''
cObj['phoneNumber'] = num
cObj['hasThumbnail'] = contact.hasThumbnail
cObj['thumbnailPath'] = contact.thumbnailPath
cts.push(cObj)
}
}
})
} else {
if (n1 != num) {
n1 = num
let cObj = {}
cObj['fullName'] =
contact.givenName +
' ' +
(contact.givenName != contact.familyName
? contact.familyName
: '')
cObj['email'] = ''
cObj['phoneNumber'] = num
cObj['hasThumbnail'] = contact.hasThumbnail
cObj['thumbnailPath'] = contact.thumbnailPath
cts.push(cObj)
}
}
})
} else {
if (
contact.emailAddresses != null &&
contact.emailAddresses.length > 0
) {
contact.emailAddresses.filter(email => {
let cObj = {}
cObj['fullName'] =
contact.givenName +
' ' +
(contact.givenName != contact.familyName
? contact.familyName
: '')
cObj['phoneNumber'] = ''
cObj['email'] = email ? (email.email ? email.email : '') : ''
cObj['hasThumbnail'] = contact.hasThumbnail
cObj['thumbnailPath'] = contact.thumbnailPath
cts.push(cObj)
})
}
}
})
return cts
}
return null
}
export default class Contacts extends Component {
constructor (props) {
super(props)
}
componentDidMount () {
request(PERMISSIONS.ANDROID.READ_CONTACTS).then(result => {
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
'This feature is not available (on this device / in this context)'
)
break
case RESULTS.DENIED:
console.log(
'The permission has not been requested / is denied but request able'
)
break
case RESULTS.GRANTED:
console.log('The permission is granted')
Contacts.getAll((err, contacts) => {
if (err === 'denied') {
// error
} else {
// read contacts here...
let filteredContacts = contactsFilter(contacts)
console.log(filteredContacts)
}
})
break
case RESULTS.BLOCKED:
console.log('The permission is denied and not request able anymore')
break
}
})
}
render () {
return (
<View>
<Text>READING CONTACTS</Text>
</View>
)
}
}
Once everything is added, just use contactsFilter for filtering contacts recieved using the react-native-contacts package. And console.log them as above. You will see the printing array with each contact as object containing fullName, phoneNumber, email, hasThumbnail and thumbnailPath as keys.
That’s it guys. Hope you loved this article.