iPhone - Xcode - Invalid CFArrayRef when searching Address Book for a symbol - iphone

I'm trying to filter contacts using a search bar each time a user enters a character. This works fine for regular characters, but when a symbol is entered like $ or ( for the first character, ABAddressBookCopyPeopleWithName returns an "Invalid CFArrayRef". I've searched a while for the solution and I can't seem to find anything. The code is below.
CFArrayRef filteredContacts = ABAddressBookCopyPeopleWithName(addressBook, (CFStringRef)searchText);
CFMutableArrayRef filteredContactsMutable = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(filteredContacts), filteredContacts);
CFArraySortValues(filteredContactsMutable, CFRangeMake(0, CFArrayGetCount(filteredContactsMutable)), (CFComparatorFunction)ABPersonComparePeopleByName, (void*)sortOrdering);
contacts = (NSArray *)filteredContactsMutable;
CFRelease(filteredContacts);
I get an EXC_BAD_ACCESS on the second line where filteredContactsMutable is created using filteredContacts. When I debug it, filteredContacts is an Invalid CFArrayRef when a symbol is the first character. The native PeoplePicker created by Apple allows you to search a symbol as the first character, so I know it's possible.
Any input would be appreciated.

first you should not trigger too much OS calls when you start search. load all data once and store it into array then search. and the reason why it crashes it due to nil argument in it cz i thnk no such user name find. try with simple charectrs n tell me if this prblm still exists

Related

For anyone familiar with the "TableViewSuite" sample code in apple dev center

In the "sample code" in iOS Dev Center there is a TableViewSuite.
In the first example SimpleTableView the simulator provides a list of time zones by country.
I can not find the list they are pulling from!
it appears to be an array but I can't find the actual words that are coming up on the simulator screen in Xcode.
I've learned about plists and dictionarys and Arrays and simply can't find where the names are coming from.
I found this line:
// Retrieve the array of known time zone names, then sort the array and pass it to the root view controller.
NSArray *timeZones = [NSTimeZone knownTimeZoneNames];
rootViewController.timeZoneNames = [timeZones sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
Again, where is it retrieving the information from?
Thanks again.
They're coming from this message:
[NSTimeZone knownTimeZoneNames]
Which is documented as such:
Returns an array of strings listing the IDs of all the time zones known to the system.
So essentially they're predefined somewhere in iOS and the names are simply being queried off the system.
+[NSTimeZone knownTimeZoneNames] gets the list of time zones from the system itself. There is no list in the project.

kABPersonFirstNameProperty... trowing EXC_BAD_ACCESS

Im reading the address book contacts... everything goes well until I test a contact with no
First Name ( Since I can create a contact with just an email or a phone or wathever....).
The code (reduced) is this:
- (NSMutableArray *) getContactsInfo {
NSMutableArray *contactsList = [[NSMutableArray alloc] init];
localAddressBook = ABAddressBookCreate();
int contactsLength = (int)ABAddressBookGetPersonCount(localAddressBook);
if (contactsLength < 1)
return nil;
for(int currentContact=1; currentContact < (contactsLength + 1); currentContact++) {
ABRecordRef person = ABAddressBookGetPersonWithRecordID(localAddressBook,(ABRecordID) currentContact);
firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSLog(#"%#", firstName);
[contactsList addObject:firstName];
CFRelease(person);
}
return contactsList;
}
and the output I get is this:
2010-02-15 14:16:25.616 testApp[7065:207] Contact0
2010-02-15 14:16:25.618 testApp[7065:207] Contact1
2010-02-15 14:16:25.619 testApp[7065:207] Contact2
Program received signal: “EXC_BAD_ACCESS”.
I have 3 contacts with First and Last names
And one created with just the last name, for test purposes.
It seems I can properly read any property such as email or address with arrays... but when a contact lacks First Name Property the app crashes.
You are doing something very wrong in your code: you are assuming that the record IDs are sequential and starting at 1. This is not the case at all, you cannot rely on this.
What you should do instead is use ABAddressBookCopyArrayOfAllPeople to find all records in the Address Book and then use the Core Foundation CFArray functions to get to the individual items.
(Yes, the Address Book API in the iPhone is terrible)
You may want to enable NSZombies to see exactly where the EXEC_BAD_ACCESS is coming from.
To make sure, the crash is taking place within ABRecordCopyValue and not when you try to use firstName for the first time (which may be NULL?) Also, person is not NULL either, correct? (In general more code in the question along with details about which line is crashing would be helpful.)
Another thing to try would be to cast person to an ABRecord* and use [valueForProperty][1]; the two types are toll-free bridged and you might get a different result out of the latter (though I doubt it).
Update: Given the code you have posted you need to check that firstName is not NULL before trying to output it via NSLog - it is very possible ABRecordCopyValue is simply returning NULL (representing the fact there is no first name data present for that record.) You should also check for the validity of the person ref value itself - passing NULL in person to ABRecordCopyValue could be the source of additional problems.
The problem is indeed a nil first name - but not at the log statement, rather where you try to insert nil into the array. You cannot insert a nil value into an array, that causes a crash. NSLog has not flushed output to the console yet which is why you do not yet see your last log statement saying first name is nil.
Any time you get data out of the address book, check to see if the value is nil before you insert it into anything.

iPhone: Reading Text From File and UISegmentedControl

First off, I'm a complete beginner.
That said, I thought an ambitious longer-term project/learning experience would be to create an app that displayed daily quotes, like those cheesy day-by-day calendars our grandmothers have in their bathrooms. I want it to have two per day, each one represented by a tab in a UISegmentedControl. That's the long term. Right now I'd be happy with getting a single day's worth of quotes functioning.
Onto the questions:
How can I get text saved in a .txt or .rtf file to be displayed in a UITextView? Preferably without using 'stringWithContentsOfFile,' since Xcode is telling me that's deprecated.
How can I get content from a different file (or maybe a different portion of the same file...?) to be displayed when the user taps the second segment?
If I can get it running so that those two conditions are met and I understand what's going on, I'll consider the day a success. Thanks!
1.
NSError *error = nil;
NSStringEncoding stringEncoding;
NSString *fileText = [NSString stringWithContentsOfFile:#"/path" usedEncoding:&stringEncoding error:&error];
myTextView.text = fileText;
The error and encoding are optional, and you can pass in nil for both. But if you care about the error, or what encoding the file was in they will have useful info in them after the string is created.
2.
Set the valueChanged outlet in Interface Builder to an IBAction on your controller, such as setSegmentValue:. Then, assuming you have an array of quote strings:
- (IBAction)setSegmentValue:(id)sender {
UISegmentedControl *control = (UISegmentedControl*)sender;
NSString *quote = [quotes objectAtIndex:control.selectedSegmentIndex];
myTextView.text = quote;
}
Even though stringWithContentsOfFile: is deprecated, stringWithContentsOfFile:usedEncoding:error: is not. That is the standard method to use for reading from files.
As for the second question, you simply test the state of the segmented control and perform as action based on it. Admittedly this is a high level answer but should get you going.

AutoCompletion on UISearchBar

I am developing applcation with UISearchBar & TableView. On UISearchBar's text enter event I am reading char. & for entered text I am geeting results from database. But problem is that it is kind of blocking call. so I have wait till result gets back but again I pressess next char. I have to query from database. For each chars enter, I have query with entire text enterd from database. I want to implement AutoCompeltion like google search bar. As In google search bar, to get list for entred chars user has to wait.
Iwant that to be implemented in my applcation. I am using iPhoneSDK 3.0.
Same thing is running while I am pressing BackSpace. but the problem is that on iPhone Simulatior it application crashes if I press BackSpace continuously.
Can anybody give me hint ???
You could always implement your text lookup on a separate thread -- it might not offer suggestions as often, but it at least wouldn't block.
If you do this, make sure to "remember" the text the lookup is based on -- then, if the text in the UISearchBar no longer matches it, throw out the results you had -- the will no longer apply.
Example:
The user has typed "bri". Tour lookup determines that possible suggestions are "bridle", "bridge", "bride" and "brigand". If by the time your lookup returns the user has added a 'd', you don't want to suggest "brigand" any more. You don't necessarily have to throw out the whole list, but you want to at least remove the items that no longer work.

Is there any way to get the "Me" card from iPhone Address Book API?

So I'm stumped on this one.
In Mac OS X there is an easy way to get the "Me" card (the owner of the Mac/account) from the built-in address book API.
Has anyone found a way to find out which contact (if it exists) belongs to the owner of the iPhone?
You could use the undocumented user default:
[[NSUserDefaults standardUserDefaults] objectForKey:#"SBFormattedPhoneNumber"];
and then search the address book for the card with that phone number.
Keep in mind that since the User Default is undocumented, Apple could change at any time and you may have trouble getting into the App Store.
Another approach you could take, although it is much more fragile, is to look at the device name. If the user hasn't changed it from the default "User Name's iPhone" AND they are using their real name as an iPhone, you could grab the user name from that. Again, not the best solution by any means, but it does give you something else to try.
The generally accepted answer to this question is to file a Radar with Apple for this feature and to prompt users to choose their card.
Contacts container have a me identifier property on iOS that can be accessed using container.value(forKey: "meIdentifier")
if let containers = try? CNContactStore().containers(matching: nil) {
containers.forEach { container in
if let meIdentifier = container.value(forKey: "meIdentifier") as? String {
print("Contacts:", "meIdentifier", meIdentifier)
}
}
The identifier is a legacy identifier used in the old AddressBook framework. You can still access it in CNContact:
let iOSLegacyIdentifier = contact.value(forKey: "iOSLegacyIdentifier")
There is no such API in the iPhone SDK 2.2.1 and earlier. Please file a request for it at: http://bugreport.apple.com
Edit: [Obsolete answer]
There's no API for getting the "me" card because there is no "me" card. The iPhone's contacts app has no way of marking a card as being "me", and the API reflects this.
I came up with a partial solution to this
you can get the device name as follows
NSString *ownerName = [[UIDevice currentDevice] name];
in English a device is originally called, for example, 'Joe Blogg's iPhone'
the break out the name
NSRange t = [ownerName rangeOfString:#"’s"];
if (t.location != NSNotFound) {
ownerName = [ownerName substringToIndex:t.location];
}
you can then take that name and search the contacts
CNContactStore *contactStore = [CNContactStore new];
NSPredicate *usersNamePredicate = [CNContact predicateForContactsMatchingName:usersName];
NSArray * keysToFetch = #[[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName],CNContactPhoneNumbersKey,CNContactEmailAddressesKey,CNContactSocialProfilesKey, ];
NSArray * matchingContacts = [contactStore unifiedContactsMatchingPredicate:usersNamePredicate keysToFetch:keysToFetch error:nil];
Of course other languages differ in the device name string e.g. 'iPhone Von Johann Schmidt' so more parsing needs to be done for other languages and it only works if the user hasn't changed the name of the device in iTunes to something like "Joes phone' but it gives you a starting point
well... it gives you an array of matching items :) So if there is more than one contact with that array you just have to use pot luck and go with the first one or work thru multiple cards and take what you need from each.
I did say its a partial solution and even though it won't work for all user cases you might find it works for many of your users and reduces a little friction