(Android and iOS) Expo Contacts List's first 20 or so contacts are ordered by area code, then resume alphabetical order. Why?

Please provide the following:

  1. SDK Version: 32.0.0
  2. Platforms(ios/android/both): both!

Expo Contacts List’s first 20 or so contacts are ordered by area code, then resume alphabetical order.

It doesn’t appear to be because of number formatting, or whether or not a contact has only first name, or first and last.

iOS goes through 30 contacts, up to area code 424, then starts alphabetically.
Android goes through 16 contacts, up to area code 415, then starts alphabetically.

I’m rather dumb founded on this.

Has anyone else experienced this?
Anyone have any ideas as to why?

imports

import React from "react";
import {
  StyleSheet,
  Text,
  View,
  ImageBackground,
  ScrollView
} from "react-native";
import { Contacts, Permissions, Linking } from "expo";
// import Contacts from "expo-contacts";
import {
  widthPercentageToDP as wp,
  heightPercentageToDP as hp
} from "react-native-responsive-screen";
import { connect } from "react-redux";
import constants from "../../constants";
import ContactsList from "../ui/ContactsList";
import TwoButtonOverlay from "../ui/TwoButtonOverlay";
import defaultStyle from "../../styles/defaultStyle";
import { parsePhoneNumber } from "../../util/util";
import CampaignLobbyHeader from "../ui/CampaignLobbyHeader";
import { TextAlt } from "../text";
import styled from "styled-components/native";

const { c } = constants;

class InvitePlayers extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      contacts: {},
      contactsFetched: false,
      invites: {},
      numInvites: 0,
      selected: {},
      numSelected: 0
    };
  }

  componentDidMount = () => {
    this.getContacts();
    const link = Linking.makeUrl("invite", {
      campaignId: this.props.campaign.id
    });
  };

getContactsAsync function

getContacts = async () => {
    const { status, permissions } = await Permissions.askAsync(
      Permissions.CONTACTS
    );
    if (status === "granted") {
      const { data } = await Contacts.getContactsAsync({
        fields: [
          Contacts.Fields.Name,
          Contacts.Fields.PhoneNumbers,
          Contacts.Fields.Image
        ], 
        sort: Contacts.SortTypes.FirstName 
      });
      if (data.length > 0) {
        const contacts = {};
        data.forEach(contact => {
          const { name } = contact;
          const numbers = [];
          let key;
          const { imageAvailable } = contact;
          let imageUri;
          if (contact.phoneNumbers) {
            contact.phoneNumbers.forEach(num => {
              if (num.label === "mobile" || num.label === "") {
                const phoneToAdd = num.number;
                key = parsePhoneNumber(phoneToAdd);
                numbers.push(phoneToAdd);
              }
            });
          }
          contact.imageAvailable
            ? (imageUri = contact.image.uri)
            : (imageUri = "none");
          if (numbers.length > 0) {
            contacts[key] = {
              id: key,
              name,
              numbers,
              imageAvailable,
              imageUri,
              invited: false,
              selected: false
            };
          }
        });
        await this.setState({ contacts });
        await this.setState({ contactsFetched: true });
      } else {
        throw new Error("No contacts found");
      }
    } else {
      throw new Error("Contacts permission not granted.");
    }
  };

Picture of contacts list

Have you tried using console.log() on the data constant after:

      const { data } = await Contacts.getContactsAsync({
        fields: [
          Contacts.Fields.Name,
          Contacts.Fields.PhoneNumbers,
          Contacts.Fields.Image
        ], 
        sort: Contacts.SortTypes.FirstName 
      });

What does the list look like before it goes into the next if and loop statements? Also use .length on the array to see if everything is there before it goes in for processing.

My initial thoughts are:

  • Something went wrong at contact.phoneNumbers.forEach()
  • Async.
  • Setting the state.
1 Like

Hi @siddemo ! Thanks for getting back to me, I appreciate your ideas - it has helped. What’s going on is that ‘data’ is returned from the async call sorted by name, but then later we create a new object ‘contacts’ who’s keys are the phone numbers, so it in effect gets resorted by phone number.

Turns out not a problem with expo sdk.

Thank you!