joshuapinter / react-native-unified-contacts

Your best friend when working with the latest and greatest Contacts Framework in iOS 9+ in React Native.
MIT License
158 stars 56 forks source link

[Android] Contacts showing twice #74

Closed paintedbicycle closed 6 years ago

paintedbicycle commented 6 years ago

When I load contacts on Android, each contact shows up twice.

screen shot 2018-05-13 at 5 55 24 pm
joshuapinter commented 6 years ago

Can you post the raw object responses for each contact? I want to see what the ids and unique values are.

paintedbicycle commented 6 years ago

Yes, I'll fire it back up. I'm 90% sure the Android code from this library is returning the contacts twice somehow (rather than it being on the app side) - I've confirmed in both my app and the example app. One sec and I'll see what the full object logs out.

paintedbicycle commented 6 years ago
 [{ birthday: null,
        emailAddresses: [],
        identifier: '2',
        fullName: 'Tester McTestName',
        id: 2,
        displayName: 'Tester McTestName',
        postalAddresses: 
         [ { label: 'Home',
             formattedAddress: '123 Fake Street, Fake City, Fake State, M4K2K2, Canada',
             pobox: null,
             street: '123 Fake Street, Fake City, Fake State, M4K2K2, Canada',
             neighborhood: null,
             type: 'Home',
             city: null,
             state: null,
             region: null,
             postalCode: null,
             postcode: null,
             country: null,
             stringValue: '123 Fake Street, Fake City, Fake State, M4K2K2, Canada' } ],
        givenName: 'Tester',
        familyName: 'McTestName',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: [],
        identifier: '2',
        fullName: 'Tester McTestName',
        id: 2,
        displayName: 'Tester McTestName',
        postalAddresses: 
         [ { label: 'Home',
             formattedAddress: '123 Fake Street, Fake City, Fake State, M4K2K2, Canada',
             pobox: null,
             street: '123 Fake Street, Fake City, Fake State, M4K2K2, Canada',
             neighborhood: null,
             type: 'Home',
             city: null,
             state: null,
             region: null,
             postalCode: null,
             postcode: null,
             country: null,
             stringValue: '123 Fake Street, Fake City, Fake State, M4K2K2, Canada' } ],
        givenName: 'Tester',
        familyName: 'McTestName',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] } ]

The above is from the example app (not my app)

joshuapinter commented 6 years ago

Weird. They have the same id values. You'd think Android would be smarter than return duplicate records.

I'm not seeing this on my ExampleApp. If you add another test Contact, do you get 4 contacts in total, 2 duplicates?

paintedbicycle commented 6 years ago

You're not seeing this on your ExampleApp? So weird.

Yes, I'm seeing 4 contacts for 2 in both my app and my ExampleApp.

Are you running 8.0? I'm running 8.0 in emulator.

joshuapinter commented 6 years ago

No, it just shows up once for each Contact, just like in the example GIF in the README.

Running 8.1 (API 27) on the emulator. I added the contacts manually, but my contacts only have a name. That's it. Maybe it has to do with the Address. Try adding a new Contact with just a name and see if you get a duplicate as well.

paintedbicycle commented 6 years ago

Lol, yup that's it.

screen shot 2018-05-13 at 6 36 35 pm
joshuapinter commented 6 years ago

Ack, okay, we'll have to figure this out.

paintedbicycle commented 6 years ago

It also applies to phone and email.

screen shot 2018-05-13 at 6 45 02 pm
paintedbicycle commented 6 years ago

Interesting - if there are 2, it adds even another contact

screen shot 2018-05-13 at 6 47 51 pm

So it appears that each time you do those methods where you push new details onto the contact, it's actually returning another contact.

joshuapinter commented 6 years ago

Looks like a similar issue to https://github.com/expo/expo/issues/877.

paintedbicycle commented 6 years ago

OK weird - I totally disagree with the admins of those two libraries that it should be handled in application code. I'm not sure the logic there. If I ask a library to return my contacts, how could I ever have expected it to turn duplicates? Why would 100 app developers all have to come up with a fix for one library? Seems weird.

This would also mean iOS and Android aren't returning the same thing...

paintedbicycle commented 6 years ago

What does https://github.com/wix/react-native-paged-contacts do?

joshuapinter commented 6 years ago

I agree. I think the library should handle it. Either we need to modify the query in such a way that it doesn't return duplicate contacts or (more likely) we need to do some post-query processing that merges contacts together before returning over the bridge to React Native.

paintedbicycle commented 6 years ago

I would have expected it to have progressively merged data into one object and return that. But yes before or after I don’t know. What would be faster? Is there a performance part of this?

paintedbicycle commented 6 years ago

I wouldn't be surprised if that Wix one held some answers. I scanned it, but the Java is a bit over me, but they have each of the queries and metadata broken into sections

joshuapinter commented 6 years ago

You're probably right, merging as we go probably makes the most sense. Not sure about performance but it doesn't look like there's any other way. Frustrating that the Android SDK doesn't just handle this for us.

I wish I could spend a week dedicated to getting this all sorted but, alas, daytime job. :)

paintedbicycle commented 6 years ago

Haha, yeah I understand. Only so much time. But putting a few hours here and there will get us there. I think if we can solve this one and the matching postal address formats between iOS and Android (https://github.com/joshuapinter/react-native-unified-contacts/issues/52) then this contact module will solve the headaches that many other fail to. Since iOS and Android will then return exactly the same thing across the board, it'll be pretty solid. I've tried 4 other libraries in my app and none of them offered complete parity.

Slowly filling in the other methods over time can wait. I can jump in and try to help out with some of them. But this one and #52 are out of my league.

paintedbicycle commented 6 years ago

Seems positive: https://stackoverflow.com/questions/39185338/android-duplicate-contact-data-while-retrieving-contacts-using-contactscontrac

joshuapinter commented 6 years ago

Yup, that looks like processing it as you're looping through it. I think that's what we'll have to do. In our case I think we'll look at the identifier or id and if it already exists in our Array, just merge the records. it's not going to be easy, though, since we're dealing with all of those different attributes, not just the Name and Phone Numbers like in that StackOverflow example.

paintedbicycle commented 6 years ago

Not to mention that we have to standardize the return object/array between iOS and Android

paintedbicycle commented 6 years ago

You know what's interesting about this bug? Each of the duplicate contacts are identical. Yes, the IDs are the same, but if you look at the content, the content is also identical.

Original I assumed that each contact ID duplicate had different content (one with the phone number, one with the email address, one with the postalAddress). But no, it appears that they are all identical, meaning it should be as simple as returning only one of them - but it wouldn't matter which one. I'd imaging the first or last in the array.

We'd have to double check that this is true, but if so, it should be a LOT easier than we thought to fix this.

joshuapinter commented 6 years ago

Now that is VERY interesting.

Are you able to test this more thoroughly? With different phone numbers, addresses, email addresses, etc.?

If all we have to do is not return Contacts more than once we’ll be in great shape! I might also be able to do a quick comparison check of the two contact records to make sure that they are the same.

Let me know what you find and I’ll write the code to do that!

On May 16, 2018, 7:56 AM -0600, Paul Wright notifications@github.com, wrote:

You know what's interesting about this bug? Each of the duplicate contacts are identical. Yes, the IDs are the same, but if you look at the content, the content is also identical. Original I assumed that each contact ID duplicate had different content (one with the phone number, one with the email address, one with the postalAddress). But no, it appears that they are all identical, meaning it should be as simple as returning only one of them - but it wouldn't matter which one. I'd imaging the first or last in the array. We'd have to double check that this is true, but if so, it should be a LOT easier than we thought to fix this. — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

paintedbicycle commented 6 years ago

Ok I did some more tests. I created several contacts:

So looks like what I found was true. You can safely just add only one of each of the IDs to the array before returning.

joshuapinter commented 6 years ago

And you confirmed that the "Duplicates" are equivalent? They have all of the same information?

paintedbicycle commented 6 years ago

Yeah, but I only did so by eye.

The below array is not in one piece, I've pieced it together from a few, but you can give it a scan.

[ { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'onlyemail@gmail.com',
             value: 'onlyemail@gmail.com' } ],
        identifier: '2',
        fullName: 'Tester OnlyEmail',
        id: 2,
        displayName: 'Tester OnlyEmail',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'OnlyEmail',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'onlyemail@gmail.com',
             value: 'onlyemail@gmail.com' } ],
        identifier: '2',
        fullName: 'Tester OnlyEmail',
        id: 2,
        displayName: 'Tester OnlyEmail',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'OnlyEmail',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: [],
        identifier: '1',
        fullName: 'Test OnlyName',
        id: 1,
        displayName: 'Test OnlyName',
        postalAddresses: [],
        givenName: 'Test',
        familyName: 'OnlyName',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: [],
        identifier: '3',
        fullName: 'Tester OnlyPhone',
        id: 3,
        displayName: 'Tester OnlyPhone',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'OnlyPhone',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: [],
        identifier: '3',
        fullName: 'Tester OnlyPhone',
        id: 3,
        displayName: 'Tester OnlyPhone',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'OnlyPhone',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: [],
        identifier: '4',
        fullName: 'Tester OnlyPostal',
        id: 4,
        displayName: 'Tester OnlyPostal',
        postalAddresses: 
         [ { label: 'Home',
             formattedAddress: '1234 Fake Street, Toronto, Ontario, M4K2K2, Canada',
             pobox: null,
             street: '1234 Fake Street, Toronto, Ontario, M4K2K2, Canada',
             neighborhood: null,
             type: 'Home',
             city: null,
             state: null,
             region: null,
             postalCode: null,
             postcode: null,
             country: null,
             stringValue: '1234 Fake Street, Toronto, Ontario, M4K2K2, Canada' } ],
        givenName: 'Tester',
        familyName: 'OnlyPostal',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: [],
        identifier: '4',
        fullName: 'Tester OnlyPostal',
        id: 4,
        displayName: 'Tester OnlyPostal',
        postalAddresses: 
         [ { label: 'Home',
             formattedAddress: '1234 Fake Street, Toronto, Ontario, M4K2K2, Canada',
             pobox: null,
             street: '1234 Fake Street, Toronto, Ontario, M4K2K2, Canada',
             neighborhood: null,
             type: 'Home',
             city: null,
             state: null,
             region: null,
             postalCode: null,
             postcode: null,
             country: null,
             stringValue: '1234 Fake Street, Toronto, Ontario, M4K2K2, Canada' } ],
        givenName: 'Tester',
        familyName: 'OnlyPostal',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemail@gmail.com',
             value: 'testemail@gmail.com' } ],
        identifier: '5',
        fullName: 'Tester PhoneANDEmail',
        id: 5,
        displayName: 'Tester PhoneANDEmail',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'PhoneANDEmail

        { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemail@gmail.com',
             value: 'testemail@gmail.com' } ],
        identifier: '5',
        fullName: 'Tester PhoneANDEmail',
        id: 5,
        displayName: 'Tester PhoneANDEmail',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'PhoneANDEmail',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemail@gmail.com',
             value: 'testemail@gmail.com' } ],
        identifier: '5',
        fullName: 'Tester PhoneANDEmail',
        id: 5,
        displayName: 'Tester PhoneANDEmail',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'PhoneANDEmail',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemail@gmail.com',
             value: 'testemail@gmail.com' } ],
        identifier: '5',
        fullName: 'Tester PhoneANDEmail',
        id: 5,
        displayName: 'Tester PhoneANDEmail',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'PhoneANDEmail',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'fakeemail@gmail.com',
             value: 'fakeemail@gmail.com' } ],
        identifier: '8',
        fullName: 'Tester PhoneEmailPostal',
        id: 8,
        displayName: 'Tester PhoneEmailPostal',
        postalAddresses: 
         [ { label: 'Home',
             formattedAddress: '123456 Fake Street, Boston, 90210, USA',
             pobox: null,
             street: '123456 Fake Street, Boston, 90210, USA',
             neighborhood: null,
             type: 'Home',
             city: null,
             state: null,
             region: null,
             postalCode: null,
             postcode: null,
             country: null,
             stringValue: '123456 Fake Street, Boston, 90210, USA' } ],
        givenName: 'Tester',
        familyName: 'PhoneEmailPostal',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5557' } ] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'fakeemail@gmail.com',
             value: 'fakeemail@gmail.com' } ],
        identifier: '8',
        fullName: 'Tester PhoneEmailPostal',
        id: 8,
        displayName: 'Tester PhoneEmailPostal',
        postalAddresses: 
         [ { label: 'Home',
             formattedAddress: '123456 Fake Street, Boston, 90210, USA',
             pobox: null,
             street: '123456 Fake Street, Boston, 90210, USA',
             neighborhood: null,
             type: 'Home',
             city: null,
             state: null,
             region: null,
             postalCode: null,
             postcode: null,
             country: null,
             stringValue: '123456 Fake Street, Boston, 90210, USA' } ],
        givenName: 'Tester',
        familyName: 'PhoneEmailPostal',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5557' } ] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'fakeemail@gmail.com',
             value: 'fakeemail@gmail.com' } ],
        identifier: '8',
        fullName: 'Tester PhoneEmailPostal',
        id: 8,
        displayName: 'Tester Pho

        { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemailaddress@gmail.com',
             value: 'testemailaddress@gmail.com' },
           { type: 'Work',
             label: 'Work',
             address: 'another@gmail.com',
             value: 'another@gmail.com' } ],
        identifier: '7',
        fullName: 'Tester TwoEmails',
        id: 7,
        displayName: 'Tester TwoEmails',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'TwoEmails',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemailaddress@gmail.com',
             value: 'testemailaddress@gmail.com' },
           { type: 'Work',
             label: 'Work',
             address: 'another@gmail.com',
             value: 'another@gmail.com' } ],
        identifier: '7',
        fullName: 'Tester TwoEmails',
        id: 7,
        displayName: 'Tester TwoEmails',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'TwoEmails',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: 
         [ { type: 'Home',
             label: 'Home',
             address: 'testemailaddress@gmail.com',
             value: 'testemailaddress@gmail.com' },
           { type: 'Work',
             label: 'Work',
             address: 'another@gmail.com',
             value: 'another@gmail.com' } ],
        identifier: '7',
        fullName: 'Tester TwoEmails',
        id: 7,
        displayName: 'Tester TwoEmails',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'TwoEmails',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: [] },
      { birthday: null,
        emailAddresses: [],
        identifier: '6',
        fullName: 'Tester TwoPhones',
        id: 6,
        displayName: 'Tester TwoPhones',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'TwoPhones',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' },
           { type: 'Home',
             label: 'Home',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: [],
        identifier: '6',
        fullName: 'Tester TwoPhones',
        id: 6,
        displayName: 'Tester TwoPhones',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'TwoPhones',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' },
           { type: 'Home',
             label: 'Home',
             digits: null,
             stringValue: '(555) 555-5555' } ] },
      { birthday: null,
        emailAddresses: [],
        identifier: '6',
        fullName: 'Tester TwoPhones',
        id: 6,
        displayName: 'Tester TwoPhones',
        postalAddresses: [],
        givenName: 'Tester',
        familyName: 'TwoPhones',
        middleName: null,
        suffix: null,
        prefix: null,
        note: null,
        phoneNumbers: 
         [ { type: 'Mobile',
             label: 'Mobile',
             digits: null,
             stringValue: '(555) 555-5555' },
           { type: 'Home',
             label: 'Home',
             digits: null,
             stringValue: '(555) 555-5555' } ] } ]
joshuapinter commented 6 years ago

Certainly looks like it! We'll see how it works in practice when we change this.

This will make it much easier! Great catch!

joshuapinter commented 6 years ago

This should be fixed now @paintedbicycle! Give it a shot and let me know how it works for you!

joshuapinter commented 6 years ago

Close this Issue when you've confirmed it's working. Thanks.

paintedbicycle commented 6 years ago

Looks good to me!