76

Most part of AddressBook framework is deprecated in iOS 9. In the new Contacts Framework documentation only shows how to fetch records matches a NSPredicate, but what if I want all the record?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Jay Hu
  • 6,041
  • 3
  • 22
  • 33

20 Answers20

122

Both other answers do only load contacts from the container with the defaultContainerIdentifier. In a scenario, where the user has more than one container (i.e. an Exchange and an iCloud account which both are used to store contacts), this would only load the contacts from the account that is configured as the default. Therefore, it would not load all contacts as requested by the author of the question.

What you'll probably want to do instead is getting all the containers and iterate over them to extract all contacts from each of them. The following code snippet is an example of how we do it in one of our apps (in Swift):

lazy var contacts: [CNContact] = {
    let contactStore = CNContactStore()
    let keysToFetch = [
        CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
        CNContactEmailAddressesKey,
        CNContactPhoneNumbersKey,
        CNContactImageDataAvailableKey,
        CNContactThumbnailImageDataKey]

    // Get all the containers
    var allContainers: [CNContainer] = []
    do {
        allContainers = try contactStore.containersMatchingPredicate(nil)
    } catch {
        print("Error fetching containers")
    }

    var results: [CNContact] = []

    // Iterate all containers and append their contacts to our results array
    for container in allContainers {
        let fetchPredicate = CNContact.predicateForContactsInContainerWithIdentifier(container.identifier)

        do {
            let containerResults = try contactStore.unifiedContactsMatchingPredicate(fetchPredicate, keysToFetch: keysToFetch)
            results.appendContentsOf(containerResults)
        } catch {
            print("Error fetching results for container")
        }
    }

    return results
}()
flohei
  • 5,248
  • 10
  • 36
  • 61
  • @flohei Thats a good answer! But you have added "lazy" keyword? And how is it helping here? – Developer Feb 12 '18 at 10:06
  • 1
    I just used lazy-loading here, because I didn't need to load all the contacts right away. I only needed them after the user took some action. – flohei Feb 12 '18 at 13:17
  • 1
    What if I use the method enumerateContactsWithFetchRequest:error:usingBlock: instead of aboove approach.What's the difference between them? Will I get all the contacts here also? – Shyam May 30 '18 at 07:27
  • 3
    According to apple sample "ManagingContacts" "enumerateContactsWithFetchRequest" should be enough to fetch all available contact. Also fetching should not happen on main queue – Marek H Jul 01 '18 at 08:27
  • I found an issue when using contactStore.containersMatchingPredicate(nil), this function return an empty array. However there are a few contacts in the phone. This happened on an iPhone X. – 鸡肉味嘎嘣脆 Jul 02 '18 at 08:50
  • Some contacts can be in multiple containers. So you could use `Set`, or filter your `[CNContact]` for duplicates. But filtering for duplicates [does use a `Set` under the hood](https://stackoverflow.com/a/55684308/1033581), so best to use a Set directly. – Cœur Dec 27 '19 at 03:26
45

Objective-C:

//ios 9+
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted == YES) {
        //keys with fetching properties
        NSArray *keys = @[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey];
        NSString *containerId = store.defaultContainerIdentifier;
        NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
        NSError *error;
        NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
        if (error) {
            NSLog(@"error fetching contacts %@", error);
        } else {
            for (CNContact *contact in cnContacts) {
                // copy data to my custom Contacts class. 
                Contact *newContact = [[Contact alloc] init];
                newContact.firstName = contact.givenName;
                newContact.lastName = contact.familyName;
                UIImage *image = [UIImage imageWithData:contact.imageData];
                newContact.image = image;
                for (CNLabeledValue *label in contact.phoneNumbers) {
                    NSString *phone = [label.value stringValue];
                    if ([phone length] > 0) {
                        [contact.phones addObject:phone];
                    }
                }
            }
        }
    }        
}];

Also to get all contacts you can use the enumerateContactsWithFetchRequest method:

CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted == YES) {
        //keys with fetching properties
        NSArray *keys = @[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey];
        CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
        NSError *error;
        BOOL success = [store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop) {
            if (error) {
                NSLog(@"error fetching contacts %@", error);
            } else {
                // copy data to my custom Contact class. 
                Contact *newContact = [[Contact alloc] init];
                newContact.firstName = contact.givenName;
                newContact.lastName = contact.familyName;
                // etc.
            }
        }];
    }        
}];

If you want to filter contacts by name you can use this:

Obj-C:

// keys from example above
NSArray *keys = @[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey];
NSArray *cnContacts = [store unifiedContactsMatchingPredicate:[CNContact predicateForContactsMatchingName:@"John Appleseed"] keysToFetch:keys error:&error];

Swift 3:

let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])

Official documentation is here: https://developer.apple.com/reference/contacts

Eridana
  • 2,418
  • 23
  • 26
18

Using Swift and Contacts framework to fetch all contacts, including name and phone numbers

import Contacts

let store = CNContactStore()
store.requestAccessForEntityType(.Contacts, completionHandler: {
    granted, error in

    guard granted else {
        let alert = UIAlertController(title: "Can't access contact", message: "Please go to Settings -> MyApp to enable contact permission", preferredStyle: .Alert)
        alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
        self.presentViewController(alert, animated: true, completion: nil)
        return
    }

    let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactPhoneNumbersKey]
    let request = CNContactFetchRequest(keysToFetch: keysToFetch)
    var cnContacts = [CNContact]()

    do {
        try store.enumerateContactsWithFetchRequest(request){
            (contact, cursor) -> Void in
            cnContacts.append(contact)
        }
    } catch let error {
        NSLog("Fetch contact error: \(error)")
    }

    NSLog(">>>> Contact list:")
    for contact in cnContacts {
        let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName) ?? "No Name"
        NSLog("\(fullName): \(contact.phoneNumbers.description)")
    }
})

Fetching contact is slow operation, so you should not block main UI thread. Do CNContactFetchRequest on background thread. That's why I put the code into completionHandler. It's run on a background thread.

AbdelHady
  • 9,334
  • 8
  • 56
  • 83
Cody
  • 4,353
  • 4
  • 39
  • 42
  • What does that mean, *"That's why I put the code into completionHandler. It's run on a background thread."* I think this code always runs on the main thread. Try printing `Thread.current.isMainThread` in before your try block and in try block and check if it returns true. To make it run on BG, first of all, the completionHandler should be `@escaping` for BG thread and you should wrap the request in some BG Thread like this: `DispatchQueue.global(qos: .default).async { //fetch here}`. AFAIK, your code runs on Main Thread. – Rajan Maheshwari Jul 13 '21 at 03:21
12

Apple actually recommends enumerateContactsWithFetchRequest of CNContactStore to fetch all contacts and NOT unifiedContactsMatchingPredicate.

Below is the working code for Obj-C.

CNContactStore *store = [[CNContactStore alloc] init];

//keys with fetching properties
NSArray *keys = @[CNContactGivenNameKey, CNContactPhoneNumbersKey]; 
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
NSError *error;

[store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop) {

        // access it this way -> contact.givenName; etc

}];

Here is the link where apple recommends enumerate function: https://developer.apple.com/reference/contacts/cncontactstore/1403266-unifiedcontactsmatchingpredicate?language=objc#discussion

If link expired, here is what Apple wrote:

If no matches are found, this method returns an empty array (or nil in case of error). Use only the predicates from the CNContact class predicates. Compound predicates are not supported by this method. Due to unification, the returned contacts may have different identifiers than you specify. To fetch all contacts, use enumerateContactsWithFetchRequest:error:usingBlock:.

GeneCode
  • 7,545
  • 8
  • 50
  • 85
9

For Swift 4

        var results: [CNContact] = []

        let fetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactMiddleNameKey as CNKeyDescriptor, CNContactEmailAddressesKey as CNKeyDescriptor,CNContactPhoneNumbersKey as CNKeyDescriptor])

        fetchRequest.sortOrder = CNContactSortOrder.userDefault

        let store = CNContactStore()

        do {
            try store.enumerateContacts(with: fetchRequest, usingBlock: { (contact, stop) -> Void in
                print(contact.phoneNumbers.first?.value ?? "no")
                results.append(contact)

            })
        }
        catch let error as NSError {
            print(error.localizedDescription)
        }

Older version for swift var results contains all contact

let contactStore = CNContactStore()
    var results: [CNContact] = []
    do {
        try contactStore.enumerateContactsWithFetchRequest(CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactMiddleNameKey, CNContactEmailAddressesKey,CNContactPhoneNumbersKey])) {
            (contact, cursor) -> Void in
            results.append(contact)
            }
    }
    catch{
        print("Handle the error please")
    }
Jaydeep Patel
  • 1,699
  • 17
  • 30
7

Get Full Name, Email Id, Phone Number, Profile Picture and Birthday Date from Contacts Framework in iOS9

#pragma mark
#pragma mark -- Getting Contacts From AddressBook
 -(void)contactsDetailsFromAddressBook{
//ios 9+
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted == YES) {
        //keys with fetching properties
        NSArray *keys = @[CNContactBirthdayKey,CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey, CNContactEmailAddressesKey];
        NSString *containerId = store.defaultContainerIdentifier;
        NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
        NSError *error;
        NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
        if (error) {
            NSLog(@"error fetching contacts %@", error);
        } else {
            NSString *phone;
            NSString *fullName;
            NSString *firstName;
            NSString *lastName;
            UIImage *profileImage;
            NSDateComponents *birthDayComponent;
            NSMutableArray *contactNumbersArray;
            NSString *birthDayStr;
            NSMutableArray *emailArray;
            NSString* email = @"";
            for (CNContact *contact in cnContacts) {
                // copy data to my custom Contacts class.
                firstName = contact.givenName;
                lastName = contact.familyName;
                birthDayComponent = contact.birthday;
                if (birthDayComponent == nil) {
                    // NSLog(@"Component: %@",birthDayComponent);
                    birthDayStr = @"DOB not available";
                }else{
                    birthDayComponent = contact.birthday;
                    NSInteger day = [birthDayComponent day];
                    NSInteger month = [birthDayComponent month];
                    NSInteger year = [birthDayComponent year];
                    // NSLog(@"Year: %ld, Month: %ld, Day: %ld",(long)year,(long)month,(long)day);
                    birthDayStr = [NSString stringWithFormat:@"%ld/%ld/%ld",(long)day,(long)month,(long)year];
                }
                if (lastName == nil) {
                    fullName=[NSString stringWithFormat:@"%@",firstName];
                }else if (firstName == nil){
                    fullName=[NSString stringWithFormat:@"%@",lastName];
                }
                else{
                    fullName=[NSString stringWithFormat:@"%@ %@",firstName,lastName];
                }
                UIImage *image = [UIImage imageWithData:contact.imageData];
                if (image != nil) {
                    profileImage = image;
                }else{
                    profileImage = [UIImage imageNamed:@"placeholder.png"];
                }
                for (CNLabeledValue *label in contact.phoneNumbers) {
                    phone = [label.value stringValue];
                    if ([phone length] > 0) {
                        [contactNumbersArray addObject:phone];
                    }
                }
                ////Get all E-Mail addresses from contacts
                for (CNLabeledValue *label in contact.emailAddresses) {
                    email = label.value;
                    if ([email length] > 0) {
                        [emailArray addObject:email];
                    }
                }
                //NSLog(@"EMAIL: %@",email);
                NSDictionary* personDict = [[NSDictionary alloc] initWithObjectsAndKeys: fullName,@"fullName",profileImage,@"userImage",phone,@"PhoneNumbers",birthDayStr,@"BirthDay",email,@"userEmailId", nil];
                // NSLog(@"Response: %@",personDict);
                [self.contactsArray addObject:personDict];
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.tableViewRef reloadData];
            });
        }
    }
}];
}
Mannam Brahmam
  • 2,225
  • 2
  • 24
  • 36
  • but am not getting names for some contacts plz help me i'm getting no's with empty names and surnames. – user3306145 May 18 '16 at 10:22
  • @user3306145, Are you using same code what ever i mentioned on top – Mannam Brahmam May 18 '16 at 10:23
  • You're the only person to mention by using above code you're unable to fetch contact names (Firstname & Lastname).. Please check your address book whether you have all contacts are saved by Reference of Firstname & LastName – Mannam Brahmam May 18 '16 at 10:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/112273/discussion-between-mannam-brahmam-and-user3306145). – Mannam Brahmam May 18 '16 at 10:57
6

@rocolitis 's answer in swift! His answer is the most correct way of doing this according to Apple's documentation.

let contactStore = CNContactStore()
let keys = [CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactNicknameKey] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: keys)

try? contactStore.enumerateContacts(with: request) { (contact, error) in

    // Do something with contact

}

You should probably check your access to your contacts first!

let authorization = CNContactStore.authorizationStatus(for: CNEntityType.contacts)

switch authorization {
case .authorized: break
case .denied: break
case .restricted: break
case .notDetermined: break
}
Duncan Babbage
  • 19,972
  • 4
  • 56
  • 93
Reid
  • 734
  • 8
  • 15
  • 1
    I could NOT figure out "what" keysToFetch was supposed to be. Never occurred to me that all I had to do was cast the array of strings as an array of CNKeyDescriptor. Thank you! – ghostatron Feb 03 '17 at 02:26
  • No problem! Please upvote my answer so more people can see it! – Reid Mar 24 '17 at 23:37
6

In swift 3 and Xcode 8 you can get all contacts list

let keys = [CNContactGivenNameKey ,CNContactImageDataKey,CNContactPhoneNumbersKey]
        var message: String!
        //let request=CNContactFetchRequest(keysToFetch: keys)
        let contactsStore = AppDelegate.AppDel.contactStore
        // Get all the containers
        var allContainers: [CNContainer] = []
        do {
            allContainers = try contactsStore.containers(matching: nil)
        } catch {
            print("Error fetching containers")
        }



        // Iterate all containers and append their contacts to our results array
        for container in allContainers {
            let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)

            do {
                let containerResults = try contactsStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keys as [CNKeyDescriptor])
                self.results.append(contentsOf: containerResults)
                self.tableView.reloadData()
                message="\(self.results.count)"
            } catch {
                print("Error fetching results for container")
            }
        }
Mahesh Giri
  • 1,810
  • 19
  • 27
5

get default container identifier first and use predicate matching container identifier

let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
let containerId = CNContactStore().defaultContainerIdentifier()
let predicate: NSPredicate = CNContact.predicateForContactsInContainerWithIdentifier(containerId)
let contacts = try CNContactStore().unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)
Jay Hu
  • 6,041
  • 3
  • 22
  • 33
  • How does this fetch _all_ the contacts? Doesn't this only fetch the contacts from the default container? What about other containers? – flohei Dec 04 '15 at 18:16
4

CNContact in iOS 9

Objective C

#import "ViewController.h"
#import <Contacts/Contacts.h>
@interface ViewController ()
{
  NSMutableArray *arrayTableData;
}

@end

@implementation ViewController

-(void)viewDidLoad
{
  [self fetchContactsandAuthorization];
}


//This method is for fetching contacts from iPhone.Also It asks authorization permission.
-(void)fetchContactsandAuthorization
{
   // Request authorization to Contacts
   CNContactStore *store = [[CNContactStore alloc] init];
   [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
   if (granted == YES)
   {
      //keys with fetching properties
      NSArray *keys = @[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey];
      NSString *containerId = store.defaultContainerIdentifier;
      NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
      NSError *error;
      NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
      if (error) 
      {
         NSLog(@"error fetching contacts %@", error);
      } 
      else 
      {
            NSString *phone;
            NSString *fullName;
            NSString *firstName;
            NSString *lastName;
            UIImage *profileImage;
            NSMutableArray *contactNumbersArray = [[NSMutableArray alloc]init];
            for (CNContact *contact in cnContacts) {
                // copy data to my custom Contacts class.
                firstName = contact.givenName;
                lastName = contact.familyName;
                if (lastName == nil) {
                    fullName=[NSString stringWithFormat:@"%@",firstName];
                }else if (firstName == nil){
                    fullName=[NSString stringWithFormat:@"%@",lastName];
                }
                else{
                    fullName=[NSString stringWithFormat:@"%@ %@",firstName,lastName];
                }
                UIImage *image = [UIImage imageWithData:contact.imageData];
                if (image != nil) {
                    profileImage = image;
                }else{
                    profileImage = [UIImage imageNamed:@"person-icon.png"];
                }
                for (CNLabeledValue *label in contact.phoneNumbers) {
                    phone = [label.value stringValue];
                    if ([phone length] > 0) {
                        [contactNumbersArray addObject:phone];
                    }
                }
                NSDictionary* personDict = [[NSDictionary alloc] initWithObjectsAndKeys: fullName,@"fullName",profileImage,@"userImage",phone,@"PhoneNumbers", nil];
                [arrayTableData addObject:[NSString stringWithFormat:@"%@",[personDict objectForKey:@"fullName"]]];
                NSLog(@"The contactsArray are - %@",arrayTableData);
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                [tableViewContactData reloadData];
            });
        }
    }
}];
}
@end

The output is

The contactsArray are - (
"John Appleseed",
"Kate Bell",
"Anna Haro",
"Daniel Higgins",
"David Taylor",
"Hank Zakroff"
}
user3182143
  • 9,459
  • 3
  • 32
  • 39
  • its only show some of the contacts.. how to fetch all the contacts on my iPhone..... – Hari Narayanan Sep 23 '16 at 13:29
  • Check it from your original device – user3182143 Sep 23 '16 at 14:21
  • Thank you @user3182143 i am used contact fetch request to solve the issue CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys]; and then save it in array [store enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) { [contact_array addObject:contact]; //add objects of all contacts list in array NSLog(@"mutable_copy count : %lu",(unsigned long)contact_array.count); }]; – Hari Narayanan Sep 23 '16 at 15:36
4

Update 1:
Here is the Swift 5 version:

lazy var contacts: [CNContact] = {
        let contactStore = CNContactStore()
        let keysToFetch: [CNKeyDescriptor] = [
            CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
            CNContactPostalAddressesKey as CNKeyDescriptor,
            CNContactEmailAddressesKey as CNKeyDescriptor,
            CNContactPhoneNumbersKey as CNKeyDescriptor,
            CNContactImageDataAvailableKey as CNKeyDescriptor,
            CNContactThumbnailImageDataKey as CNKeyDescriptor]

        // Get all the containers
        var allContainers: [CNContainer] = []
        do {
            allContainers = try contactStore.containers(matching: nil)
        } catch {
            print("Error fetching containers")
        }

        var results: [CNContact] = []

        // Iterate all containers and append their contacts to our results array
        for container in allContainers {
            let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)

            do {
                let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch)
                results.append(contentsOf: containerResults)
            } catch {
                print("Error fetching results for container")
            }
        }

        return results
    }()

Original Answer:
Here is the Swift 3.0 version of flohei's answer

lazy var contacts: [CNContact] = {
        let contactStore = CNContactStore()
        let keysToFetch = [
            CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
            CNContactPostalAddressesKey,
            CNContactEmailAddressesKey,
            CNContactPhoneNumbersKey,
            CNContactImageDataAvailableKey,
            CNContactThumbnailImageDataKey] as [Any]
        
        // Get all the containers
        var allContainers: [CNContainer] = []
        do {
            allContainers = try contactStore.containers(matching: nil)
        } catch {
            print("Error fetching containers")
        }
        
        var results: [CNContact] = []
        
        // Iterate all containers and append their contacts to our results array
        for container in allContainers {
            let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
            
            do {
                let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
                results.append(contentsOf: containerResults)
            } catch {
                print("Error fetching results for container")
            }
        }
        
        return results
    }()

Hope this helps!

S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
3

SWIFT 2

Fetch Full Name, Email Id, Phone Number, Profile Picture from Contacts Framework in iOS9

NOTE Contacts without name has also been handled.

Step 1

import Contacts

Step 2

func fetchContacts(completion: (result: NSMutableArray) -> Void  )
    {
        let finalArrayForContacts = NSMutableArray()
        let contactsArray = NSMutableArray()
        let requestForContacts = CNContactFetchRequest(keysToFetch: [CNContactIdentifierKey, CNContactFormatter.descriptorForRequiredKeysForStyle(CNContactFormatterStyle.FullName), CNContactPhoneNumbersKey ,CNContactThumbnailImageDataKey])
          do{
            try contactStore.enumerateContactsWithFetchRequest(requestForContacts) { (contactStore : CNContact, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
                contactsArray.addObject(contactStore)
            }
        }
        catch {

        }
        if contactsArray.count > 0 {
            let formatter = CNContactFormatter()
            for contactTemp  in contactsArray
            {
                let contactNew = contactTemp as! CNContact
                //Contact Name
                var stringFromContact = formatter.stringFromContact(contactNew)
                if stringFromContact == nil {
                    stringFromContact = "Unnamed"
                }


                var imageData = NSData?()
                if contactNew.thumbnailImageData != nil{
                     imageData = contactNew.thumbnailImageData!
                }else{
//         imageData = nil
                }
                var tempArray : NSArray = NSArray()
                if (contactNew.phoneNumbers).count > 0 {
                    tempArray = ((contactNew.phoneNumbers as? NSArray)?.valueForKey("value").valueForKey("digits")) as! NSArray
                    for i in 0  ..< tempArray.count
                    {
                      let newDict = NSMutableDictionary()
                        let phoneNumber : String = (tempArray.objectAtIndex(i)) as! String

                        if phoneNumber.characters.count > 0 {
                            var test = false

                            if phoneNumber.hasPrefix("+")
                            {
                                test = true
                            }
                            var resultString : String = (phoneNumber.componentsSeparatedByCharactersInSet(characterSet) as NSArray).componentsJoinedByString("")

                            if test == true
                            {
                                resultString = "+\(resultString)"
                            }
                            newDict.setValue(resultString, forKey: "contact_phone")
                            newDict.setValue(stringFromContact, forKey: "contact_name")
                            newDict.setValue("0", forKey: "contact_select")
                             newDict.setValue(imageData, forKey: "contact_image")
                            finalArrayForContacts.addObject(newDict)
                        }
                    }
                }else{
                    // no number saved
                }
            }
        }else {
            print("No Contacts Found")
        }
        completion(result: finalArrayForContacts)
    }
Maninderjit Singh
  • 1,419
  • 1
  • 13
  • 23
2

I am trying this code it works fine. I can fetch all contacts details using this code in swift3 latest framework using contacts:

let requestForContacts = CNContactFetchRequest(keysToFetch: [CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName), CNContactPhoneNumbersKey as CNKeyDescriptor ,CNContactImageDataKey as CNKeyDescriptor,CNContactEmailAddressesKey as CNKeyDescriptor,CNContactBirthdayKey as CNKeyDescriptor])
do {
    try self.store.enumerateContacts(with: requestForContacts) { contact, stop in
        print("contact:\(contact)")
        self.contacts.append(contact)
    }
} catch {
    print(error)
}

for contact in self.contacts {
    print(contact)
    let firstName = contact.givenName
    nameArray.append(firstName)
    print("first:\(firstName)")
    let phoneNumber = (contact.phoneNumbers[0].value).value(forKey: "digits")
    phoneNumberArray.append(phoneNumber as! String)
    let emailAddress = contact.emailAddresses[0].value(forKey: "value")
    emailAddressArray.append(emailAddress as! String)
}
Racil Hilan
  • 24,690
  • 13
  • 50
  • 55
2

Cody's response in Swift 3:

import Contacts

Then within whatever function you're using:

           let store = CNContactStore()
            store.requestAccess(for: .contacts, completionHandler: {
                granted, error in

                guard granted else {
                    let alert = UIAlertController(title: "Can't access contact", message: "Please go to Settings -> MyApp to enable contact permission", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                    self.present(alert, animated: true, completion: nil)
                    return
                }

                let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey] as [Any]
                let request = CNContactFetchRequest(keysToFetch: keysToFetch as! [CNKeyDescriptor])
                var cnContacts = [CNContact]()

                do {
                    try store.enumerateContacts(with: request){
                        (contact, cursor) -> Void in
                        cnContacts.append(contact)
                    }
                } catch let error {
                    NSLog("Fetch contact error: \(error)")
                }

                print(">>>> Contact list:")
                for contact in cnContacts {
                    let fullName = CNContactFormatter.string(from: contact, style: .fullName) ?? "No Name"
                    print("\(fullName): \(contact.phoneNumbers.description)")
                }
            })
user1333394
  • 696
  • 6
  • 6
1

Right now in iOS9 ABAddressBookRef is deprecated so to fetch all contact from phone use this framework and add this function you will get array of contact.

import Contact framework in .h class like this

#import <Contacts/Contacts.h>

then add this method in .m file

 -(void)contactsFromAddressBook{
//ios 9+
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted == YES) {
        //keys with fetching properties
        NSArray *keys = @[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey];
        NSString *containerId = store.defaultContainerIdentifier;
        NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
        NSError *error;
        NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
        if (error) {
            NSLog(@"error fetching contacts %@", error);
        } else {
            NSString *phone;
            NSString *fullName;
            NSString *firstName;
            NSString *lastName;
            UIImage *profileImage;
            NSMutableArray *contactNumbersArray;
            for (CNContact *contact in cnContacts) {
                // copy data to my custom Contacts class.
                firstName = contact.givenName;
                lastName = contact.familyName;
                if (lastName == nil) {
                    fullName=[NSString stringWithFormat:@"%@",firstName];
                }else if (firstName == nil){
                    fullName=[NSString stringWithFormat:@"%@",lastName];
                }
                else{
                    fullName=[NSString stringWithFormat:@"%@ %@",firstName,lastName];
                }
                UIImage *image = [UIImage imageWithData:contact.imageData];
                if (image != nil) {
                    profileImage = image;
                }else{
                    profileImage = [UIImage imageNamed:@"person-icon.png"];
                }
                for (CNLabeledValue *label in contact.phoneNumbers) {
                    phone = [label.value stringValue];
                    if ([phone length] > 0) {
                        [contactNumbersArray addObject:phone];
                    }
                }
                NSDictionary* personDict = [[NSDictionary alloc] initWithObjectsAndKeys: fullName,@"fullName",profileImage,@"userImage",phone,@"PhoneNumbers", nil];
                [MutableArray__Contact addObject:personDict];
            }
            dispatch_async(dispatch_get_main_queue(), ^
            {
                NSLog(@"%@",ar_Contact);
                //[self.tableViewRef reloadData];
            });
        }
    }
}];
}

for using this method call contactsFromAddressBook function

[self contactsFromAddressBook];
user3189586
  • 125
  • 1
  • 8
  • hello i'm using NSArray *contactList = [NSArray arrayWithArray:[CNContactVCardSerialization contactsWithData:dataString error:nil]]; CNContact *contactObject = [contactList objectAtIndex:0]; and here i'm unable to get image of contact UIImage *image = [UIImage imageFormData:contactObject.imageData.]; plzz help... – Abhishek Thapliyal Mar 26 '16 at 09:08
  • check for permission CNContactImageDataKey also check is there image present for person . – user3189586 Mar 28 '16 at 12:38
  • image is there but when i'm converting it it image from data it is getting null – Abhishek Thapliyal Mar 28 '16 at 12:41
  • i'm parsing by vcf file – Abhishek Thapliyal Mar 28 '16 at 13:00
  • you can use this if image is not null NSData *imgData = (NSData *)ABPersonCopyImageData(person); UIImage *img = [UIImage imageWithData:imgData]; – user3189586 Mar 29 '16 at 04:07
1

Permissions for Contacts iOS 9 SWIFT 2

 let status : CNAuthorizationStatus = CNContactStore.authorizationStatusForEntityType(CNEntityType.Contacts)
        if status == CNAuthorizationStatus.NotDetermined{
            contactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (temp: Bool,  error : NSError?) -> Void in
             //call contacts fetching function
            })
        }else if status == CNAuthorizationStatus.Authorized {
             //call contacts fetching function
            })
        }
        else if status == CNAuthorizationStatus.Denied {
            }
    }
Maninderjit Singh
  • 1,419
  • 1
  • 13
  • 23
1

@flohei answer in Swift-4

 var contacts: [CNContact] = {
        let contactStore = CNContactStore()
        let keysToFetch = [
            CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName),
            CNContactEmailAddressesKey,
            CNContactPhoneNumbersKey,
            CNContactImageDataAvailableKey,
            CNContactThumbnailImageDataKey] as [Any]

        // Get all the containers
        var allContainers: [CNContainer] = []
        do {
            allContainers = try contactStore.containers(matching: nil)
        } catch {
            print("Error fetching containers")
        }

        var results: [CNContact] = []

        // Iterate all containers and append their contacts to our results array
        for container in allContainers {
            let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)

            do {
                let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
                results.append(contentsOf: containerResults)
            } catch {
                print("Error fetching results for container")
            }
        }

        return results
    }()
Kiran Jasvanee
  • 6,362
  • 1
  • 36
  • 52
1

Just wanted to share this versions of Swift 4

info.plist:

<key>NSContactsUsageDescription</key>
    <string>$(PRODUCT_NAME) requires to access your contacts ...</string>

module:

import Contacts

code:

func fetchContacts(completion: @escaping (_ result: [CNContact]) -> Void){
        DispatchQueue.main.async {
            var results = [CNContact]()
            let keys = [CNContactGivenNameKey,CNContactFamilyNameKey,CNContactMiddleNameKey,CNContactEmailAddressesKey,CNContactPhoneNumbersKey] as [CNKeyDescriptor]
            let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
            fetchRequest.sortOrder = .userDefault
            let store = CNContactStore()
            store.requestAccess(for: .contacts, completionHandler: {(grant,error) in
                if grant{
                    do {
                        try store.enumerateContacts(with: fetchRequest, usingBlock: { (contact, stop) -> Void in
                            results.append(contact)
                        })
                    }
                    catch let error {
                        print(error.localizedDescription)
                    }
                    completion(results)
                }else{
                    print("Error \(error?.localizedDescription ?? "")")
                }
            })
        }
    }

Usage:

fetchContacts(completion: {contacts in
            contacts.forEach({print("Name: \($0.givenName), number: \($0.phoneNumbers.first?.value.stringValue ?? "nil")")})

You need to describe the usage info in info.plist first. I have added a check to determine user has granted the access to contacts then defined the keys (the values needed to be fetched). As said in one of the earlier answers that it's a time consuming process so I have added DispatchQueue for background processing and completion handler for returning the contacts array back to caller.

Sahil Manchanda
  • 9,812
  • 4
  • 39
  • 89
  • What does `DispatchQueue.main.async` do? The processing for fetching and populating your results is still on the main thread. So even when DispatchQueue.main.async is an async call, a heavy-duty operation added in it can freeze the UI as its operations are serially executed on the main thread. I think in place of `DispatchQueue.main.async`, you should write something like `DispatchQueue.global(qos: .default).async { //fetch here}` and when your completion returns, then grab the main thread and populate your UI. – Rajan Maheshwari Jul 13 '21 at 03:30
1

Swift 4.2 . Fetch contact numbers with image

info.plist file data
<key>NSContactsUsageDescription</key>
<string>$(PRODUCT_NAME) requires to access your contacts ...</string>



//MARK:- Fetch All Contacts of Phone
func fetchContacts(completion: @escaping (_ result: [CNContact]) -> Void){
    DispatchQueue.main.async {
        var results = [CNContact]()
        let keys = [CNContactGivenNameKey,CNContactFamilyNameKey,CNContactMiddleNameKey,CNContactEmailAddressesKey,CNContactPhoneNumbersKey,CNContactThumbnailImageDataKey] as [CNKeyDescriptor]
        let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
        fetchRequest.sortOrder = .userDefault
        let store = CNContactStore()
        store.requestAccess(for: .contacts, completionHandler: {(grant,error) in
            if grant{
                do {
                    try store.enumerateContacts(with: fetchRequest, usingBlock: { (contact, stop) -> Void in
                        results.append(contact)
                    })
                }
                catch let error {
                    print(error.localizedDescription)
                }
                completion(results)
            }else{
                print("Error \(error?.localizedDescription ?? "")")
            }
        })
    }
}

}

Function Calling in Did Load Method

var arrpic = NSMutableArray()

 override func viewDidLoad() {
    super.viewDidLoad()

    fetchContacts(completion: {contacts in
        contacts.forEach({print("Name: \($0.givenName), number: \($0.phoneNumbers.first?.value.stringValue ?? "nil")")
            self.arrfname.append("\($0.givenName)")
            self.arrlname.append("\($0.familyName)")
            self.arrnumber.append("\($0.phoneNumbers.first?.value.stringValue ?? "nil")")
            var img = UIImage()
            if $0.thumbnailImageData != nil
            {
                img = UIImage.init(data: $0.thumbnailImageData!)!
                self.arrpic.add(img)
            }
            else
            {
                self.arrpic.add("")
            }
        })
        if contacts.count > 0
        {
            self.tablev.reloadData()
        }
    })
}
Shakeel Ahmed
  • 5,361
  • 1
  • 43
  • 34
0

If you want to get ALL fields of a contact with known identifier:

let contact = unifiedContact(withIdentifier: identifier, keysToFetch: [CNContactVCardSerialization.descriptorForRequiredKeys()])

This gives you an access to ALL fields, such as adresses, phone numbers, full name, etc.

To retrieve fullName then:

let fullname = CNContactFormatter.string(from: contact, style: .fullName)
Mari
  • 109
  • 1
  • 3