Your best friend when working with the latest and greatest Contacts Framework in iOS 9+.
Apple recently did a complete overhaul of their Contacts Framework that does a number of things, including:
-
Making it simpler to use the framework.
-
Use the same framework across all their platforms, including iOS, tvOS, watchOS and even OS X.
-
Get unified Contact details not only from a User's local Contact entry, but also from the user's social accounts, like Facebook and Twitter. This allows you to get a Facebook profile picture for a Contact you have in your contact database.
There are a couple of react native packages that give you access to Contacts already (react-native-contacts and react-native-addressbook) but neither of them utilize the new Unified Contacts framework available in iOS 9+. For good reason too, as most people are still supporting devices that are pre-iOS 9.
However, if you have the luxury of supporting iOS 9 and up, you should definitely use this library to make use of this great new framework by Apple.
- Install the npm package:
npm install --save react-native-unified-contacts
This will install the latest react-native-unified-contacts package and add it to your package.json file.
- Navigate to
<your-project-directory>/node_modules/react-native-unified-contacts/
and drag theRNUnifiedContacts
directory into your project directory in Xcode.
Ensure that `Copy items if needed` is **not** checked, select `Create groups` and ensure your project is selected for a target.
![Select files](readme_assets/drag_and_drop_library_to_sidebar.gif)
- For iOS 10+, you need to add a
NSContactsUsageDescription
key to yourInfo.plist
that provides a reason why your app needs to access private information:
<key>NSContactsUsageDescription</key>
<string>ntwrk accesses Contacts in order to quickly add Relationships and allow them to reach out via ntwrk through email, text, phone, etc.</string>
If done through XCode UI, the key is named Privacy - Contacts Usage Description
.
- See the Usage section for how to require the library and make use of it.
If done through XCode UI, the key is named Privacy - Contacts Usage Description
.
var Contacts = require('react-native-unified-contacts');
let contactIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
Contacts.getContact( contactIdentifier, (error, contact) => {
if (error) {
console.error(error);
}
else {
console.log(contact);
}
});
Contacts.getContacts( (error, contacts) => {
if (error) {
console.error(error);
}
else {
console.log(contacts);
}
});
Contacts.searchContacts( 'Don Draper', (error, contacts) => {
if (error) {
console.error(error);
}
else {
console.log(contacts);
}
});
This will search the given (first), family (last) and nick names of all of the contacts for the provided string. Future versions will allow you to search other fields as well, like phone or email.
let contactData = {
'givenName': 'John',
'familyName': 'Appleseed',
'organizationName': 'Apple Inc',
'phoneNumbers': [
{'label': Contacts.phoneNumberLabel.HOME, 'stringValue': '555-522-8243'},
{'label': Contacts.phoneNumberLabel.WORK, 'stringValue': '(408) 555-5270'},
],
'emailAddresses': [
{'label': Contacts.emailAddressLabel.WORK, 'value': '[email protected]'},
{'label': Contacts.emailAddressLabel.HOME, 'value': '[email protected]'},
],
}
Contacts.addContact( contactData, (error, success) => {
if (error) {
console.log(error);
}
else {
console.log(success);
}
});
let contactIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
let contactData = {
'givenName': 'John',
'familyName': 'Appleseed',
'organizationName': 'Apple Inc',
'phoneNumbers': [
{'label': Contacts.phoneNumberLabel.HOME, 'stringValue': '555-522-8243'},
{'label': Contacts.phoneNumberLabel.WORK, 'stringValue': '(408) 555-5270'},
],
'emailAddresses': [
{'label': Contacts.emailAddressLabel.WORK, 'value': '[email protected]'},
{'label': Contacts.emailAddressLabel.HOME, 'value': '[email protected]'},
],
}
Contacts.updateContact(contactIdentifier, contactData, (error, success) => {
if (error) {
console.log(error);
}
else {
console.log(success);
}
});
NOTE: If your contactData
includes the keys phoneNumbers
or emailAddresses
, the associated value will completely replace any Phone Numbers or Email Addresses for that Contact, respectively. In other words, if you have a contact with two Phone Numbers and you'd like to add a third, you need to pass in ALL THREE Phone Numbers, not just the new one. Same goes for Email Addresses.
let contactIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
Contacts.deleteContact( contactIdentifier, (error, success) => {
if (error) {
console.log(error);
}
else {
console.log(success);
}
}
let groupIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
Contacts.getGroup( groupIdentifier, (error, group) => {
if (error) {
console.error(error);
} else {
console.log(group);
}
});
Contacts.getGroups( (error, groups) => {
if (error) {
console.error(error);
}
else {
console.log(groups);
}
});
let groupIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
Contacts.contactsInGroup(groupIdentifier, (error, contacts) => {
if (error) {
console.error(error);
} else {
console.log(contacts);
}
});
let groupData = {
'name': 'Friends'
}
Contacts.addGroup(groupData, (error, success) => {
if (error) {
console.log(error);
}
else {
console.log(success);
}
});
let groupIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
let groupData = {
'name': 'Friends'
}
Contacts.updateGroup(groupIdentifier, groupData, (error, success) => {
if (error) {
console.log(error);
}
else {
console.log(success);
}
});
let groupIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
Contacts.deleteGroup(groupIdentifier, (error, success) => {
if (error) {
console.log(error);
}
else {
console.log(success);
}
});
let groupIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
let contactIdentifiers = [
'4E5R6TGH-2EWQ-SAD2-SADS-2344EWFSDCSA',
'8GFK43JK-2E3F-U6HF-UYTB-23R4TGTHYRDF',
'5FTGYUHG-DSFD-4T5H-43TR-0IOJUVBHJNVG'
]
Contacts.addContactsToGroup(groupIdentifier, contactIdentifiers, (error, success) => {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
let groupIdentifier = 'A7806266-6574-4731-82E1-C54946F63E1C';
let contactIdentifiers = [
'4E5R6TGH-2EWQ-SAD2-SADS-2344EWFSDCSA',
'8GFK43JK-2E3F-U6HF-UYTB-23R4TGTHYRDF',
'5FTGYUHG-DSFD-4T5H-43TR-0IOJUVBHJNVG'
]
Contacts.removeContactsFromGroup(groupIdentifier, contactIdentifiers, (error, success) => {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
Contacts.userCanAccessContacts( (userCanAccessContacts) => {
if (userCanAccessContacts) {
console.log("User has access to Contacts!");
}
else {
console.log("User DOES NOT have access to Contacts!");
}
});
This will not request access. For that, use the requestAccessToContacts
.
Contacts.requestAccessToContacts( (userCanAccessContacts) => {
if (userCanAccessContacts) {
console.log("User has access to Contacts!");
}
else {
console.log("User DOES NOT have access to Contacts!");
}
});
This will do everything you'd expect. Here's the workflow:
-
Does the user already have access to Contacts?
-
Yes. Return
true
. -
No.
-
If the User has not been asked before (first time asking), prompt user for access:
-
Yes. Return
true
. -
No. Return
false
.
-
-
If user has already denied access to Contacts, return
false
.The user will have to go to their privacy settings and allow access manually. We provide a
openPrivacySettings
method that allows you to bring up the privacy page easily for the user. See below.
-
Contacts.openPrivacySettings()
In the event that the User has denied access to their Contacts, you will need to have them manually change their setting in the privacy page. This method will open up the right page automatically for them and improves the experience for the user.
Here's an example of how you might alert the user that they need to update their privacy settings:
// Alert the User that we can't access their Contact.
// Provide a link that will open up their Privacy Settings for ntwrk.
//
function alertUserToAllowAccessToContacts() {
Alert.alert(
"Can't Access Your Contacts",
"Click on Open Settings and allow ntwrk to access your Contacts.\n" +
"\n" +
"Then come back!",
[
{text: 'Open Settings', onPress: () => Contacts.openPrivacySettings() },
{text: "Later"}
]
)
}
This will produce an alert similar to this:
The returned Contact object(s) will look something like this:
{
"contactRelations": [
{
"label": "Mother",
"identifier": "2D103009-45E2-4CA5-A2D5-50642EE7430C",
"name": "Mother Name",
"localizedLabel": "mother"
}
],
"middleName": "Middle Name",
"phoneticGivenName": "Phonetic Given Name",
"phoneticMiddleName": "Phonetic Middle Name",
"nickname": "Nickname",
"phoneticFamilyName": "Phonetic Family Name",
"previousFamilyName": "Previous Family Name",
"familyName": "Family Name",
"phoneNumbers": [
{
"label": "Home",
"identifier": "73A0E4B6-86F6-4FAF-A7C3-D55705CA1DFF",
"stringValue": "1 (234) 567-8901",
"countryCode": "us",
"digits": "12345678901",
"localizedLabel": "home"
}
],
"identifier": "D296E8E2-982F-4C39-9845-B75808B9CF96:ABPerson",
"givenName": "Given Name",
"dates": [
{
"label": "Anniversary",
"identifier": "FB73491F-E505-4864-8B68-F5F17C5AC4D7",
"year": 2016,
"month": 1,
"day": 30,
"localizedLabel": "anniversary"
}
],
"fullName": "Full Name",
"nonGregorianBirthday": {
"year": 33,
"month": 1,
"day": 3
},
"departmentName": "Department Name",
"socialProfiles": [
{
"label": "twitter",
"localizedLabel": "twitter",
"service": "Twitter",
"localizedService": "Twitter",
"urlString": "http://twitter.com/12345",
"userIdentifier": "",
"username": "12345",
"identifier": "53698CFA-0125-48FE-84D7-E8372CD99ECD"
}
],
"emailAddresses": [
{
"label": "Home",
"identifier": "8AE516A9-A531-4BBA-B890-BFA59E47A4C7",
"value": "[email protected]",
"localizedLabel": "home"
}
],
"instantMessageAddresses": [
{
"label": "Skype",
"identifier": "82200A8E-4F19-4F5A-A7AF-FA9F8C43B01A",
"service": "Skype",
"localizedService": "Skype",
"username": "12345",
"localizedLabel": "Skype"
}
],
"note": "Note",
"postalAddresses": [
{
"label": "Home",
"city": "City",
"state": "CA",
"localizedLabel": "home",
"postalCode": "98765",
"country": "United States",
"isoCountryCode": "",
"stringValue": "123 St\nCity CA 98765\nUnited States",
"street": "123 St",
"identifier": "7A472311-AB4F-46F4-B046-B60143DBC858"
}
],
"urlAddresses": [
{
"label": "HomePage",
"identifier": "13F46F96-E5EE-4B6F-A8A6-EBC3C459E314",
"value": "www.example.com",
"localizedLabel": "homepage"
}
],
"organizationName": "Organization Name",
"birthday": {
"month": 1,
"day": 30
},
"nameSuffix": "Name Suffix",
"imageDataAvailable": false,
"contactType": "person",
"namePrefix": "Name Prefix",
"jobTitle": "Job Title"
}
NOTE: The birthday key will not be included if the Contact's birthday is not set. Also, it's possible for a Contact's
birthday to not include the year
. In this case, year
will be null
.
Thumbnail Image Data is stored in a base64 format and can easily be used with the Image
component of React Native as follows:
// contact is a single Contact record retrieved from something like Contacts.getContacts().
var base64ImageUri = 'data:image/png;base64,' + contact.thumbnailImageData;
<Image source={{uri: base64ImageUri}}/>
If you run into trouble, take a look at the following thread:
You should also have the latest version of XCode (8.2+) and Swift (3+).
If that doesn't help you, please create an Issue and we'll figure it out together.
- My friend Smixx for working through adding a Swift library to a React Native project over his lunch hour.
- Ismail Pelaseyed (homanp) for adding a couple of huge PRs for Creating, Updating and Deleting Contacts.
- Chris Edwards (chrise86) for adding a tonne of big PRs for Adding, Updating and Deleting Groups.
- Add Create/Update/Delete methods for Contacts. (Thanks homanp!)
- Add Android support.
- Add integration with Contacts-UI (Coming Soon!).
The MIT License (MIT)
Copyright 2016 - Time.now()
by Joshua Pinter