With the AddressBook API, the only way to get a list of all of the people seems to be via 'ABAddressBookCopyArrayOfAllPeople' if you're not using 'ABPeoplePickerNavigationController'.
ABPeoplePickerNavigationController's pick a person and dismiss the view behavior is not what I need. Instead, what I'd like to do is load the address book contents into my own table view and then do some custom selection behavior.
But is there any way to deal with partially loading the address book data instead of the entire list to supply the data as a the table's data source? If say the address book contained 3000 entries, then I'm copying all of that data into a local array -- is there a more optimal way around this to achieve better performance?
Not sure its optimal but if you seek to show all the content in your table, it must be "sorted" somehow,
suppose you simple sort it alphabetically, then you can instead of copy all the addressbook just ask for it to return a list of records starting with "a"
the method for this is I think:
CFArrayRef ABAddressBookCopyPeopleWithName (
ABAddressBookRef addressBook,
CFStringRef name
);
name can be a "a*" which would return all people starting with a.
multiple calls to this method from your table could be used to avoid copying all people I think.
hope this helps,
-t
Related
I have a tableview that is of type PFQueryTableViewController and gets its value from Parse cloud. The tableview works fine but now I need to allow the user to:
Select a row
Record in a column (string array) on Parse-Users table what rows have been selected (need to record on parse that - i will use these values for other things later)
When the user comes back and opens the tableview he can see what rows have been selected last time he was in the app
I am not sure if PFQueryTableViewController has any methods ready for that. Could anyone give me some guidance?
I would prefer to use parse cause there are so much stuff out of the box. But if not, that is fine as well.
Also, code samples from similar solutions would be great. Just need to know the best approach.
The table view controller is there for display, it will tell you about selection, but it won't automatically maintain a record of selected items in the back end. You need to decide on the appropriate way to store the selections (array of pointers is better than an array of strings) and update the store and table display appropriately. There is no standard approach to this.
Given that an ABRecordID can change between cloud syncs and under other circumstances out of my control, how can I maintain a long-term reference to an IOS address book record?
Apple provides the following guidance:
"The recommended way to keep a long-term reference to a particular record is to store the first and last name, or a hash of the first and last name, in addition to the identifier. When you look up a record by ID, compare the record’s name to your stored name. If they don’t match, use the stored name to find the record, and store the new ID for the record."
But I don't understand this guidance. If the address book can have duplicate names in it AND since users can modify the name in a record how could this advice work?
For example, if the user modifies the name of an address book record my routine will fail to find it by ABRecordID so if I think search by the name hash I stored couldn't I find a duplicate name instead of the new ABRecordID for that specific record I previously referenced?
In the end, what is the BEST way to get a long-term reference to an IOS AddressBook record? And if the above advice really does work what am I missing?
The most robust (yet not completely failsafe) approach would be to come up with a priority ranking of ABRecord fields and store as much from that list as is available, along with the ABRecordID, into your own (hashed) private record format. When retrieving a private record (or at another convenient time), you can verify that the private record matches the ABRecord and work through a series of fallback checks to ensure it's accurate.
Example priority ranking:
ABRecordID
FirstName
LastName
PhoneNumber
ZipCode
When retrieving a record you can first match the ABRecordID. If that returns no results, you can do a search for FirstName + LastName. You can then match those results against PhoneNumber... etc. In this way you could potentially distinguish between 2 Bob Smiths, as they may have different phone numbers (or one may not have a phone number). Of course, depending on how long your priority list is, the more robust this mechanism will be.
The last resort would be prompting the user to distinguish between 2 Bob Smiths with brand new ABRecordID's whose records are otherwise identical -- after all, such an inconvenient prompt would be far more friendly than allowing the User to contact the wrong Bob Smith (and as I said, would be a last resort).
This solution for AB may involve some synchronization issues, however.
This is a familiar problem for anyone who has worked with the iOS Media Player. Specifically MPMediaItems in the User's Music Library have a property MPMediaItemPropertyPersistentID which the docs describe as:
The value is not guaranteed to persist across a sync/unsync/sync cycle.
In other words, the PersistentID is not guaranteed to be persistent. Solutions for this include doing similar fallback checks on MediaItem properties.
The RecordID only get changed either on delete or reset, when this is done all the new record(s) will have new createdProperty and modifiedProperty as well.
While I am reading the address book for the first time, I will save all entries of the record along with RecordID in my database.
I will save the last time the contacts synced from contacts to my database(name it something: lastSyncedTime) and store it some where.
I am done with syncing the contacts for the first time, now do the following for syncing anytime in future.
while Iterating through all records,
check createdTime(kABPersonCreationDateProperty) vs lastSyncedTime. If createdTime > lastSyncedTime, store the recordID in a "newRecords" NSArray.
If !(step 1) then check modifiedDate(kABPersonModificationDateProperty) vs lastSyncedTime. If modifiedDate > lastSyncedTime, then store the recordID in a "modifiedRecords" NSArray.
if !(1) && !(2) store all recordID in a "unModifiedRecords".
Now I will read all the contacts from my local database,
I will delete all local database records that are not find either in "modifiedRecords" or in "unModifiedRecords".
I will update all "modifiedRecords" in the local database.
I will create new records for all records in "newRecords".
Update the lastSyncedTime accordingly.
The documentation is communicating to you that you can't count on ABRecordID as a permanent identifier.
Consider this scenario: The user has a record for "Bob Smith". The user then deletes his "Bob Smith" record and then imports his contacts from his computer (creating a new ID) through iTunes sync.
So if you want to keep a permanent reference to an existing contact, you can keep a reference to the name and id as a hint that it is the same record you used before- but there is no real permanent reference.
If you keep a permanent reference to an address book contact, you must always be ready to deal with the fact that it may not be the same contact you used before.
Refer :
https://developer.apple.com/library/ios/documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Chapters/DirectInteraction.html#//apple_ref/doc/uid/TP40007744-CH6-SW2
Clearly tells you how to handle it.
I am using searchDisplayController and it would be searching for the names and also the sections. i would like to know how to show the results of only names and not including the section. Assume the codes are the similar as from apples doc. I have at least 2000 names in there when viewing the tableview itself.
See my answer to this question:
UISearchBar Search table row with text, subtext and image
What you show in a results table is completely up to you. It needn't even have anything to do with the table you're searching! (But of course it usually does, as otherwise you'd confuse the user.) You simply form the data that populates the results table; what data that is, is your call.
So, if you don't want to include any section titles, then when the table inquiring of your data source / delegate is the results table, don't include any section titles! It's your code, it's your table, do whatever you want. You are the one implementing tableView:titleForHeaderInSection: to return titles; if you don't want titles, return nil instead. Of course, if the data source for the real table is the same object as the data source for the results table, then tableView:titleForHeaderInSection: will have to examine the incoming tableView parameter to see whether it is the real table or the results table, and make its choice of what to return based on that.
I would like to a bit more of your problem as it is not much clear. I don't actually get this line:
i would like to know how to show the results of only names and not including the section
For searching, it is best to search in a dictionary/array and show the result in tableview by [tableView reloadData].
I'm looking for a way to query contacts in my iPhone app based on a few things. First I only want contacts that have a phonenumber. Second, id like to sort the contacts in order of the number of times contacted.
Android provides attributes that makes this possible and easy to do.
I can't really say that I know the answer, but I believe I know where to find the answer:
http://developer.apple.com/library/ios/#documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Introduction.html#//apple_ref/doc/uid/TP40007744-CH1-SW1
Here are some quotes from that document that seems relevant:
There are two ways to find a person record in the Address Book database: by name, using the function ABAddressBookCopyPeopleWithName, and by record identifier, using the function ABAddressBookGetPersonWithRecordID. To accomplish other kinds of searches, use the function ABAddressBookCopyArrayOfAllPeople and then filter the results using the NSArray method filteredArrayUsingPredicate:.
To sort an array of people, use the function CFArraySortValues with the function ABPersonComparePeopleByName as the comparator and a context of the type ABPersonSortOrdering. The user’s desired sort order, as returned by ABPersonGetSortOrdering, is generally the preferred context.
Those quotes were both found on this page. I hope it helps.
Hey,
I'm saving a copy of all contacts in a database.
Upon launching, I want to check if there is a new contact in the address book in order to add it to my data base.
How to perform this check? I can't find the suitable logic to implement it.
There's not an easy way to get "new contacts" in the address book. Instead you'll have to do something like this:
Get an array of all the contacts in the address book.
Loop through the array and see if the contact is in your database. (NOTE: Record ids can change!)
If you find a contact that is not in your database, add it.
It's probably also smart to store the last modification date of the property and compare that with each record so you can know if something has changed on an existing record.
Finally, this could take a while depending on the number of contacts the user has. It's probably something you want to do without blocking the UI. (Another note: Address Book types are not thread safe. Keep this in mind if you're working in a background thread.)
While your app is running, you can register address book change notification callback to be alerted of changes that happen while your app is running.
A little easier way(don't need too much memory space):
Save your App's database lastest TimeStamp when you load data from AddressBook & total person count.
write a check function, and call it whenever you need it
check function : Get the latest modification date in all contacts, and get the total count right now, just compare the latest date with TimeStamp:
BOOL beNeedRefresh = NO;
if( latest > TimeStamp){
beNeedRefresh = YES;
}else if(addressbook count != your Database count){
beNeedRefresh = YES;
}
if(beNeedRefresh){
refresh your database !!!!
}