How To Access Contacts In React Native?


How To Access Contacts In React Native?

Table Of Contents:

  1. Install Required Libraries.

(1) Install Required Libraries

  • Install the react-native-contacts library to access the contact details.
npm install [email protected]

(2) Add Required Permission

  • For Android device add permission in AndroidManifest.xml File.
  • android\app\src\main\AndroidManifest.xml
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
  • For IOS: Add these keys to Info.plist:
<key>NSContactsUsageDescription</key>
<string>Allow access to your contacts to display them in the app.</string>

(3) Implement This In Code

import React, { useEffect, useState } from "react";
import { View, Text, FlatList, TouchableOpacity, StyleSheet, SafeAreaView, TextInput, PermissionsAndroid, Platform, Alert, NativeModules } from "react-native";
import Icon from "react-native-vector-icons/FontAwesome";
import Contacts from 'react-native-contacts';
import MainLayout from "../components/MainLayout";

const ContactsScreen: React.FC = () => {
  const [deviceContacts, setDeviceContacts] = useState<any[]>([]);
  const [searchText, setSearchText] = useState<string>("");

  const requestContactsPermission = async () => {
    if (Platform.OS === 'android') {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
        {
          title: 'Contacts Permission',
          message: 'This app needs access to your contacts to display them.',
          buttonPositive: 'OK',
          buttonNegative: 'Cancel',
        }
      );

      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        return true;
      } else if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
        Alert.alert(
          'Permission Required',
          'Please enable contacts permission from settings to view contacts.',
          [{ text: 'OK' }]
        );
      }

      return false;
    }
    return true;
  };


  const fetchContacts = async () => {
    const hasPermission = await requestContactsPermission();
    if (!hasPermission) {
      console.warn('Permission denied');
      return;
    }

    try {
      const permissionStatus = await Contacts.checkPermission();
      console.log('Contacts Permission Status:', permissionStatus);
      if (permissionStatus === 'authorized') {
        const contacts = await Contacts.getAll();
        console.log('Fetched Contacts:', contacts);

        const formattedContacts = contacts.map((contact) => ({
          id: contact.recordID,
          name: contact.displayName || contact.phoneNumbers[0]?.number || 'Unknown',
          phone: contact.phoneNumbers[0]?.number || 'N/A',
        }));

        const sortedContacts = formattedContacts.sort((a, b) => {
          const isANumber = !isNaN(Number(a.name.replace(/\s/g, '')));
          const isBNumber = !isNaN(Number(b.name.replace(/\s/g, '')));

          if (isANumber === isBNumber) {
            return a.name.localeCompare(b.name);
          }
          return isANumber ? 1 : -1; // Push numbers to the bottom
        });

        setDeviceContacts(sortedContacts);
      } else {
        Alert.alert('Permission Denied', 'Contacts permission is not authorized.');
      }
    } catch (error) {
      console.error('Failed to fetch contacts:', error);
      Alert.alert('Error', 'Failed to fetch contacts');
    }
  };

  const filteredContacts = deviceContacts.filter(
    (contact) =>
      contact.name.toLowerCase().includes(searchText.toLowerCase()) ||
      contact.phone.toLowerCase().includes(searchText.toLowerCase())
  );

  useEffect(() => {
    fetchContacts();

  }, []);

  const renderContact = ({ item }: { item: typeof deviceContacts[0] }) => (
    <View style={styles.card}>
      <View style={styles.contactContainer}>
        <Icon name="user-circle" size={40} color="#00B4D8" style={styles.icon} />
        <View style={styles.textContainer}>
          <Text style={styles.name}>{item.name}</Text>
          <Text style={styles.phone}>{item.phone}</Text>
        </View>
        <Icon name="ellipsis-v" size={20} color="#999" style={styles.optionsIcon} />
      </View>
    </View>
  );

  return (
    <MainLayout>
      <SafeAreaView style={styles.safeArea}>
        <View style={styles.container}>
          <View style={styles.tabsContainer}>
            <TouchableOpacity style={[styles.tab, styles.activeTab]}>
              <Text style={[styles.tabText, styles.activeTabText]}>All Contacts</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.tab}>
              <Text style={styles.tabText}>Office</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.tab}>
              <Text style={styles.tabText}>Family</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.newGroupButton}>
              <Text style={styles.newGroupText}>+ New Group</Text>
            </TouchableOpacity>
          </View>

          <View style={styles.searchBarContainer}>
            <Icon name="search" size={20} color="#999" style={styles.searchIcon} />
            <TextInput
              placeholder="Search by name or number"
              style={styles.searchBar}
              value={searchText}
              onChangeText={setSearchText}
            />
          </View>

          <FlatList
            data={filteredContacts}
            renderItem={renderContact}
            keyExtractor={(item) => item.id}
            contentContainerStyle={styles.list}
          />

          <TouchableOpacity style={styles.addButton}>
            <Text style={styles.addButtonText}>Add New Contact</Text>
          </TouchableOpacity>
        </View>
      </SafeAreaView>
    </MainLayout>
  );
};

// YOUR EXISTING STYLES (UNCHANGED)
const styles = StyleSheet.create({
  safeArea: {
    flex: 1,
    width: '100%',
    backgroundColor: "#F0F4F8",
  },
  container: {
    width: '100%',
    flex: 1,
    backgroundColor: "#F0F4F8",
  },
  title: {
    fontSize: 28,
    fontWeight: "bold",
    color: "#333",
    marginVertical: 20,
  },
  tabsContainer: {
    flexDirection: "row",
    marginBottom: 20,
    alignItems: "center",
  },
  tab: {
    marginRight: 15,
    paddingVertical: 5,
  },
  activeTab: {
    borderBottomWidth: 2,
    borderBottomColor: "#1e90ff",
  },
  tabText: {
    fontSize: 16,
    color: "#666",
  },
  activeTabText: {
    color: "#1e90ff",
    fontWeight: "600",
  },
  newGroupButton: {
    backgroundColor: "#1e90ff",
    paddingVertical: 5,
    paddingHorizontal: 10,
    borderRadius: 12,
  },
  newGroupText: {
    color: "#fff",
    fontSize: 14,
    fontWeight: "600",
  },
  searchBarContainer: {
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "#fff",
    borderRadius: 12,
    paddingHorizontal: 10,
    marginBottom: 20,
    shadowColor: "pink",
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 2,
  },
  searchIcon: {
    marginRight: 10,
  },
  searchBar: {
    flex: 1,
    height: 40,
    fontSize: 16,
    color: "#333",
  },
  list: {
    paddingBottom: 16,
  },
  card: {
    width: '100%',
    backgroundColor: "#FFFFFF",
    padding: 20,
    borderRadius: 16,
    marginBottom: 16,
    shadowColor: "pink",
    shadowOffset: { width: 0, height: 5 },
    shadowOpacity: 0.1,
    shadowRadius: 6,
    elevation: 5,
    justifyContent: "center",
  },
  contactContainer: {
    flexDirection: "row",
    alignItems: "center",
  },
  icon: {
    marginRight: 15,
  },
  textContainer: {
    flex: 1,
  },
  name: {
    fontSize: 18,
    fontWeight: "600",
    color: "#E76F51",
  },
  phone: {
    fontSize: 16,
    color: "#555",
    marginTop: 4,
  },
  optionsIcon: {
    marginLeft: 10,
  },
  addButton: {
    backgroundColor: "#1e90ff",
    paddingVertical: 12,
    borderRadius: 12,
    alignItems: "center",
  },
  addButtonText: {
    color: "#fff",
    fontSize: 16,
    fontWeight: "600",
  },
});

export default ContactsScreen;

Leave a Reply

Your email address will not be published. Required fields are marked *