I have a view controller on my app, where the user is shown a table view. The table view displays their address book contacts and allows the user to select any friends that they would like to invite to the app.
I have 2 model classes: HALAddressBook
represents the address book, and HALContact
represents an individual contact.
HALAddressBook.h:
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
#import <AddressBook/ABAddressBook.h>
#import <AddressBook/ABPerson.h>
@interface HALAddressBook : NSObject
#pragma mark - Properties
@property NSArray *contacts;
#pragma mark - Instance Methods
- (BOOL)isAccessGranted;
@end
HALAddressBook.m:
#import "HALAddressBook.h"
@implementation HALAddressBook
- (BOOL)isAccessGranted
{
ABAddressBookRef m_addressbook = ABAddressBookCreateWithOptions(NULL, NULL);
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
// Write your code here...
// Fetch data from SQLite DB
}
});
ABAddressBookRequestAccessWithCompletion(m_addressbook, ^(bool granted, CFErrorRef error)
{
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
if (accessGranted) {
// Access has been granted
self.contacts = (__bridge NSArray *)(ABAddressBookCopyArrayOfAllPeople(m_addressbook));
return YES;
} else {
// Access has not been granted
return NO;
}
}
@end
HALContact.h:
@interface HALContact : NSObject
#pragma mark - Properties
@property NSArray *phoneNumbers;
@property NSString *mainPhoneNumber;
@property NSString *firstName;
@property ABRecordRef contactRef;
#pragma mark - Instance Variables
- (BOOL)hasMultiplePhoneNumbers;
@end
HALContact.m:
#import "HALContact.h"
@interface HALContact ()
@end
@implementation HALContact
- (BOOL)hasMultiplePhoneNumbers
{
if (self.phoneNumbers.count > 1) {
return YES;
} else {
return NO;
}
}
@end
Both of these model classes are then imported into HALAddFriendsViewController.m
, and here is how they are used:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:NO];
// Create an instance of HALAddressBook
HALAddressBook *addressBook = [[HALAddressBook alloc]init];
// Ask for access to the user's address book
BOOL result = [addressBook isAccessGranted];
if (!result) {
[self performSegueWithIdentifier:@"addFriendsToMediaCaptureSegue" sender:self];
}
// Access has been granted
// Create array of all address book contacts
NSArray *contacts = [addressBook contacts];
// Initialize our friends array
self.friends = [[NSMutableArray alloc]init];
// Loop through all of the contacts.
// Create an instance of HALContact
// Set the instance's contactRef, firstName and phoneNumbers properties
for (id currentContact in contacts) {
// Create an instance of HALContact,
// set its contactRef property,
// set its firstName property,
HALContact *contact = [[HALContact alloc]init];
contact.contactRef = (__bridge ABRecordRef)(currentContact);
contact.firstName = (__bridge_transfer NSString
*)ABRecordCopyValue(contact.contactRef, kABPersonFirstNameProperty);
// If firstName is nil, continue
if (!contact.firstName) continue;
// Grab all of the phone number values for this contact
NSArray *phoneNumberValues = (__bridge NSArray *)(ABRecordCopyValue(contact.contactRef, kABPersonPhoneProperty));
// Place a copy of all phone numbers into contact's
// phoneNumber property
contact.phoneNumbers = (__bridge NSArray *)(ABMultiValueCopyArrayOfAllValues((__bridge ABMultiValueRef)(phoneNumberValues)));
// Check if the contact has multiple phone numbers
BOOL result = [contact hasMultiplePhoneNumbers];
// If contact only has one phone number, then add the
// number from the phoneNumbers property into the
// contact's mainPhoneNumber property
if (!result) {
contact.mainPhoneNumber = contact.phoneNumbers[0];
// Add contact to friends array
[self.friends addObject:contact];
continue;
}
// Loop through the contacts phone numbers
// and for every phone number, create a copy of the contact
// and place the current phone number at index in
// the copy's mainPhoneNumber property.
// So if "Steve" has 2 phone numbers, he'll be added to our
// friends array twice, once for each phone number.
for (int index = 0; index < contact.phoneNumbers.count; index++) {
HALContact *copyOfContact = [[HALContact alloc]init];
copyOfContact.mainPhoneNumber = contact.phoneNumbers[index];
copyOfContact.firstName = contact.firstName;
// Add copyOfContact to the friends array
[self.friends addObject:copyOfContact];
}
}
// Set table view's datasource and delegate
self.tableView.dataSource = self;
self.tableView.delegate = self;
// Reload table view data
[self.tableView reloadData];
}
I am looking for an overall review, and want to see if I am understanding MVC and using it in the correct way.
Some specific things I am also concerned about:
- Is it ok that I am setting the properties of the
HALContact
instance in my view controller? - I was told that
- (BOOL)hasMultiplePhoneNumbers
should also be a property. Is this true? Is it a general best practice to backBOOL
methods with aBOOL
property? - How is my overall code quality? Everything from comments, style, etc.