0

I am trying to print out all of my phone's contacts to the console, using NSLog(). Currently this code is just printing (null).

.h

@property (nonatomic, strong) NSMutableArray *contactsObjects;

.m

@synthesize contactsObjects;
//lazy instantiation. 
- (NSMutableArray *)contactsObjects
{
    if(!contactsObjects)
    {
        contactsObjects = [[NSMutableArray alloc]init];
    }

    return contactsObjects;
}

- (void)viewWillAppear:(BOOL)animated
{
    CFErrorRef error = nil;

    // Request authorization to Address Book
    ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &error);

    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
        ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
            if (granted) {
                // First time access has been granted, add all the user's contacts to array.
                CFMutableArrayRef contactsObjects = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
            } else {
                // User denied access.
                // Display an alert telling user that they must allow access to proceed to the "invites" page.
            }
        });
    }
    else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
        // The user has previously given access, add all the user's contacts to array.
        CFMutableArrayRef contactsObjects = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
    }
    else {
        // The user has previously denied access
        // Send an alert telling user that they must allow access to proceed to the "invites" page.
    }

    NSLog(@"%@", contactsObjects);
}

I get two warnings here:

enter image description here

I have no idea what I am supposed to do in order to properly print the names and numbers of my contacts to the console.

How do I print my contacts names and numbers?

jww
  • 97,681
  • 90
  • 411
  • 885
Chisx
  • 1,976
  • 4
  • 25
  • 53
  • Don't you have another warning in `viewWillAppear:` about shadowing an ivar? Or an error about an undeclared variable pointing at the setter? – jscs Jan 13 '14 at 02:26
  • I don't actually. The two errors a "yellow !" errors, and they're just for the error I posted above. – Chisx Jan 13 '14 at 02:27
  • What version of Xcode are you on? Are you explicitly synthesizing `contactsObjects`? – jscs Jan 13 '14 at 02:28
  • I'm not sure what the explicit synthesization is, but I have synth'd it: `@synthesize contactsObjects;` – Chisx Jan 13 '14 at 02:29
  • That's explicit synthesis, yup. – jscs Jan 13 '14 at 02:30

2 Answers2

2

You say it prints out null and that you get an error. But this would explain your error.

contactObjects is defined within the if block and the else if block. So by the time you are outside of your conditional it's no longer defined.

Try this

- (void)viewWillAppear:(BOOL)animated
{
    CFErrorRef error = nil;

    // Request authorization to Address Book
    ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &error);

    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
        ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
            if (granted) {
                // First time access has been granted, add all the user's contacts to array.
                contactsObjects = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
            } else {
                // User denied access.
                // Display an alert telling user that they must allow access to proceed to the "invites" page.
            }
        });
    }
    else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
        // The user has previously given access, add all the user's contacts to array.
        contactsObjects = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
    }
    else {
        // The user has previously denied access
        // Send an alert telling user that they must allow access to proceed to the "invites" page.
    }

    NSLog(@"%@", contactsObjects);
}
ansible
  • 3,569
  • 2
  • 18
  • 29
  • `contactsObjects` almost certainly shouldn't be locally declared here; I'm pretty sure OP just wants to put it into the ivar. Other than that this is it. – jscs Jan 13 '14 at 02:31
  • I'm sorry, must be blind to my own mistake, but I do not even see a change in your code and mine. Do you mind pointing out where exactly you made change? – Chisx Jan 13 '14 at 02:32
  • Yeah, as @JoshCaswell mentioned, you would probably put this outside of the function (or as a return value) but this was the easiest way to explain the error. – ansible Jan 13 '14 at 02:35
  • That actually just caused another error. With `CFMutableArrayRef contactsObjects;` added, it is suggesting that I set the variable to NULL, to look like `CFMutableArrayRef contactsObjects = NULL;` Without or with the NULL initiating, the errors still exist. – Chisx Jan 13 '14 at 02:37
  • There's no return value from `viewWillAppear:` (Also, note that it's a warning, not an error.) Just delete the local declaration `CFMutableArrayRef contactsObjects;` Then the two references to `contactsObjects` in `viewWillAppear:` are the ivar, which is what you're trying to do, @WhiteHatPrince. – jscs Jan 13 '14 at 02:38
  • @JoshCaswell - I see, removed the declaration from my answer. – ansible Jan 13 '14 at 02:40
  • The warnings still exist. In your answer, are you also suggesting that I delete the lazy instantiation code? Ill post a picture. – Chisx Jan 13 '14 at 02:42
  • What warnings exactly are you seeing? I don't think you need to remove the lazy instantiation code. – ansible Jan 13 '14 at 02:44
  • The photo was just posted.. sorry for it's size – Chisx Jan 13 '14 at 02:46
  • Remove the `CFMutableArrayRef` from both lines (like in my example). You do not want to declare a new CFMutableArrayRef, but use the one that already exists in your class, like you are doing on your lazy instantiation code. – ansible Jan 13 '14 at 02:48
  • Ohhh, my bad man I really could not see it. Took it out and it wanted a bridge. Gave it the bridge and I'm trying it right now – Chisx Jan 13 '14 at 02:50
2

You have a scope problem with your code. The contactsObjects variables in viewWillAppear: are not related to the ivar you have called contactsObjects. You're declaring new variables that are using the same name. The NSLog() at the end, on the other hand, is the ivar. But setting those other variables didn't put anything into the ivar, so you see (null), which is how NSLog() represents "no object".

Fix this by not making new variable declarations, but using the ivar.

if (granted) {
    contactsObjects = ABAddressBookCopyArrayOfAllPeople(addressBookRef);

else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
    contactsObjects = ABAddressBookCopyArrayOfAllPeople(addressBookRef);

You will also need to cast these:

    contactsObjects = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBookRef);

(Also, the function doesn't return a mutable array, so you may have trouble down the road with that.)

The second problem is that ABAddressBookRequestAccessWithCompletion() doesn't stop and wait for its completion Block to run. While access is being requested, the rest of your method carries on, so you reach the NSLog() before contactsObjects is actually set in that case.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • Ok so, using the `(__bridge_transfer NSArray *)` gives me a warning for 'incompatible' pointer type', so I need to redeclare the variable **contactsObjects** as an `NSArray` rather than an `NSMutableArray`? And your suggesting that I need a breakpoint after the `ABAddressBookRequestAccessWithCompletion()` method? Also, (__bridge NSMutableArray *) was not causing any warnings, but it was causing the console to print out addresses rather than names or #'s – Chisx Jan 13 '14 at 03:13
  • Yes, change the property's type. Casting to `NSMutableArray` is a lie. It doesn't change the mutability of the array. You don't need a breakpoint. Breakpoints are for debugging. You just need to take into account the fact that, when you request access, `contactObjects` won't be set until after the completion Block runs, which may or may not be before `viewWillAppear:` ends. – jscs Jan 13 '14 at 04:05