DEV Community

Kaan Şan
Kaan Şan

Posted on

Tutorial: Transfering contacts with React Native

Painful problem

I always forgot to store my contacts on google or anywhere.
Sometimes I don't want to either.
When I changed my smartphone, I have to transfer my contacts essentially. Manually adding contacts is a huge pain.

pain

Research I did

I've researched some applications but unfortunately didn't like their behaviour. They were not open source or they were using bluetooth. I didn't trust some of them either.

Solution

After some researchs, I've decided to code an android app. I've used expo with react native to build a quick solution for transfering contacts.
Basically, Your old phone can send your contacts in a json format.
On your new phone, you can use that json file to compare values and import them.
This is an open source app called contacto.

https://github.com/kaansan/contacto

It might be helpful for react native beginners to read and understand, what contacto does in under the hood.

Quick Tips

contacto uses Contacts, Sharing, FileSystem, DocumentPicker, IntentLauncher as well for achieving for transfering contacts.

Every contact number on a phone can be imported with a file format called vcf. It stands for Variant Call Format.
Which I create vcf aka vCard with this function.

const getVcardTemplate = (name, number) =>`
BEGIN:VCARD
VERSION:2.1
N:;${name};;;
TEL;CELL:${number}
END:VCARD
`
Enter fullscreen mode Exit fullscreen mode

Show me the code ...

We get phone contacts data with Contacts API with expo
We have to get permission first, to get contacts then we can iterate that data to get numbers and names.
I also get the id from contacts for rendering unique objects on the FlatList

getPhoneContacts = async () => {
        const { status } = await Contacts.requestPermissionsAsync()

        if (status === 'granted') {
            // Getting contacts with permissions on android
            const { data } = await Contacts.getContactsAsync({
                fields: [
                    Contacts.PHONE_NUMBERS,
                ],
            })

            // Getting data we need.
            const contacts = []
            data.map((number) => {
                const { name, phoneNumbers, id } = number
                if (name && phoneNumbers && id) {
                    let phoneNumber = phoneNumbers[0]?.number
                    phoneNumber = phoneNumber.replace(/\s+/g, '')
                    contacts.push({ name, number: phoneNumber, id })
                }
            })

            // Let's write phone contacts to a json file.
            const jsonContacts = JSON.stringify(contacts)
            const uri = createFileName(FileSystem, 'contacts.json')
            await this.writeContactsToFile(uri, jsonContacts)
            await this.sendOldPhoneContacts(uri)
        }
    }
Enter fullscreen mode Exit fullscreen mode

Another key function for comparing values
We pick the contacts.json with DocumenPicker.
Get the permission and get new phone contacts too for comparing numbers.
Then, we have to iterate parsed contact.json and create vCard templates.
After aggregating data, we can simply write a new vcf file.

compareNumbers = async () => {
        const result = await DocumentPicker.getDocumentAsync({})

        if (result.type === 'success') {
            if (!result.name.includes('contacts.json')) {
                alert('You have to select contacts.json')
                return        
            } else {
                alert(`You've picked the file: ${result.name}`)
            }

            const { uri } = result
            if (uri) {
                try {
                    const jsonContacts = await FileSystem.readAsStringAsync(uri)
                    const parsedContacts = JSON.parse(jsonContacts)
                    const { status } = await Contacts.requestPermissionsAsync()

                    if (status === 'granted') {
                        // Getting contacts with permissions on android
                        const { data } = await Contacts.getContactsAsync({
                            fields: [
                                Contacts.PHONE_NUMBERS,
                            ],
                        })

                        const numbers = []
                        data.map((number) => {
                            const { phoneNumbers } = number
                            if (phoneNumbers) {
                                let phoneNumber = phoneNumbers[0]?.number
                                phoneNumber = phoneNumber.replace(/\s+/g, '')
                                numbers.push(phoneNumber)
                            }
                        })

                        const newContacts = []
                        let vCardTotal = ''
                        parsedContacts.map((contact) => {
                            const { name, number, id } = contact

                            // Finding unrecorded phone numbers
                            const exist = numbers.find((currentNumber) => currentNumber === number)

                            if (!exist) {
                                newContacts.push({ id, name, number })
                                const vCard = getVcardTemplate(name, number)
                                vCardTotal += vCard
                            } else {
                                console.log(`${number} is exist !`)
                            }
                        })

                        const newRecordsUri = createFileName(FileSystem, 'new_contacts.vcf')
                        this.setState({ newContacts, newRecordsUri })
                        if (vCardTotal) {
                            await this.writeContactsToFile(newRecordsUri, vCardTotal)
                        } else {
                            alert('Your contacts are up to date !')
                        }
                    }
                } catch (err) {
                    throw new Error(err)
                }
            }
        } else {
            alert('You have to give permission for comparing contacts !')
        }
    }
Enter fullscreen mode Exit fullscreen mode

Importing VCF file for adding contacts with expo is not documented.
I had to use IntentLauncher for viewing vcf file. You cant import via Linking

 importNewContacts = async () => {
        const { newRecordsUri } = this.state

        await FileSystem.getContentUriAsync(newRecordsUri).then((cUri) => {
            IntentLauncher.startActivityAsync('android.intent.action.VIEW', {
                data: cUri,
                type: 'text/x-vcard',
                flags: 1,
            })
        })
    }
Enter fullscreen mode Exit fullscreen mode

Anyways, I hope this would be helpful or resolves somenone's issue.

Top comments (0)