I am starting to work with the ABAddress Book and using a very simple starting point... I want to get all the entries in my address book and put it into an array. It keeps showing 0 elements.
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
NSArray *allContacts = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
Doesn't the ABAddressBookCopyArrayOfAllPeople actually get all people from your contact list or did I misunderstand something.
NOTE TO ADMIN's My question was a Objective C related and not for SWIFT so this answer doesn't apply. In the recommended answer it explains why the OP's code migration from Objective C to Swift is flawed and doesn't work. My question is not. I am trying to understand why when I install this on my iPhone, which has hundreds of contacts allContacts has no items. I was trying to understand the correct way to put all my contacts into an array. Basically because of business need I am doing my own contact picker because I don't want to use Apple's built in one.
First of all you are trying to use deprecated apis. This may be why ABAddressBookCreateWithOptions doesn't work.
However, my recommendation based on your edit (where you say that you want to build your own UI) is to use the Contacts framework.
See this github gist:
#import <Contacts/Contacts.h>
#implementation ContactsScan
- (void) contactScan
{
if ([CNContactStore class]) {
//ios9 or later
CNEntityType entityType = CNEntityTypeContacts;
if( [CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined)
{
CNContactStore * contactStore = [[CNContactStore alloc] init];
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
if(granted){
[self getAllContact];
}
}];
}
else if( [CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized)
{
[self getAllContact];
}
}
}
-(void)getAllContact
{
if([CNContactStore class])
{
//iOS 9 or later
NSError* contactError;
CNContactStore* addressBook = [[CNContactStore alloc]init];
[addressBook containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers: #[addressBook.defaultContainerIdentifier]] error:&contactError];
NSArray * keysToFetch =#[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey];
CNContactFetchRequest * request = [[CNContactFetchRequest alloc]initWithKeysToFetch:keysToFetch];
BOOL success = [addressBook enumerateContactsWithFetchRequest:request error:&contactError usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop){
[self parseContactWithContact:contact];
}];
}
}
- (void)parseContactWithContact :(CNContact* )contact
{
NSString * firstName = contact.givenName;
NSString * lastName = contact.familyName;
NSString * phone = [[contact.phoneNumbers valueForKey:#"value"] valueForKey:#"digits"];
NSString * email = [contact.emailAddresses valueForKey:#"value"];
NSArray * addrArr = [self parseAddressWithContac:contact];
}
- (NSMutableArray *)parseAddressWithContac: (CNContact *)contact
{
NSMutableArray * addrArr = [[NSMutableArray alloc]init];
CNPostalAddressFormatter * formatter = [[CNPostalAddressFormatter alloc]init];
NSArray * addresses = (NSArray*)[contact.postalAddresses valueForKey:#"value"];
if (addresses.count > 0) {
for (CNPostalAddress* address in addresses) {
[addrArr addObject:[formatter stringFromPostalAddress:address]];
}
}
return addrArr;
}
#end
Note: This code was not written by me. The source can be found on github.
Related
I am trying to using CoreSpotlight API in application , I have plist file which has a several items on it for example animals' name . So I need to set title string equal to on of those object , for example if users search Lion , the line name and for example its features appears on the spotlight . Here is my code :
- (void)setupCoreSpotlightSearch
{
CSSearchableItemAttributeSet *attibuteSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(__bridge NSString *)kUTTypeImage];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"animals" withExtension:#"plist"];
NSArray *playDictionariesArray = [[NSArray alloc ] initWithContentsOfURL:url];
NSString *getNames = [NSString stringWithFormat:#"%#",playDictionariesArray];
NSLog(#"%#",getNames) ;
attibuteSet.title =getNames;
attibuteSet.contentDescription = #"blah blah ";
CSSearchableItem *item = [[CSSearchableItem alloc] initWithUniqueIdentifier:#"app name"
domainIdentifier:#"com.compont.appname"
attributeSet:attibuteSet];
if (item) {
[[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:#[item] completionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(#"Search item indexed");
}
}];
}
}
The problem is getNames returns all names !!! how can I filter it when is user is searching an specific word from animals.plist
Thanks .
EDIT [Plist Image]:
You can maintain NSArray and iterate through playDictionariesArray, creating & initialising CSSearchableItem object with that particular entry in your data source.
- (void)setupCoreSpotlightSearch
{
NSURL *url = [[NSBundle mainBundle] URLForResource:#"animals" withExtension:#"plist"];
NSArray *playDictionariesArray = [[NSArray alloc ] initWithContentsOfURL:url];
NSMutableArray * searchableItems = [[NSMutableArray alloc]init];
for(object in playDictionariesArray)
{
CSSearchableItemAttributeSet *attibuteSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(__bridge NSString *)kUTTypeImage];
attibuteSet.title =[NSString stringWithFormat:#"%#",object]; //retrive title from object and add here
//attibuteSet.contentDescription = #"blah blah "; // retrieve description from object and add here
CSSearchableItem *item = [[CSSearchableItem alloc] initWithUniqueIdentifier:#"app name"
domainIdentifier:#"com.compont.appname"
attributeSet:attibuteSet];
[searchableItems addObject:item];
}
if (searchableItems) {
[[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:searchableItems completionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(#"Search item indexed");
}
}];
}
}
I haven't ran and tested the code.
you are not looping through each key. Use the code provided in this question.
CoreSpotlight indexing
Try it on a device that supports indexing.
NOT iPhone 4/4s or iPad.
There are lots of applications doing AddressBook synchronization with their Backend servers to cross check which contacts in your AddressBook are using their application and which users needs to be invited to their application.
For the first time it may do a full sync, but after that it shouldn't be a full sync.
My first question is, What is the best way to sync the full AddressBook with a Backend Server?
Second question is, How to sync ONLY the contacts which has modified recently?
If there's any sample application or a tutorial please share with me.
Thanks in advance.
Hello i am also working on same concept.
First of all i don’t have any tutorial to share with you. But what i am doing in application i am sharing with you entire flow.
When application runs first time i am fetching all the contact numbers and sending to server.
But when any contact modifies i am sending modified contact number only.
You will get a call back from address book when contact is modified. There will be specific record id for each contact
Swift
typealias ABExternalChangeCallback = CFunctionPointer<((ABAddressBook!, CFDictionary!,UnsafeMutablePointer) -> Void)>
From this property you will come to know when your contact was modified recently.
ABRecordCopyValue(record, kABPersonModificationDateProperty).takeRetainedValue() as? NSDate
So either way you can check for contact numbers which were modified recently and then again just send those contact to server.
I hope it may helps you!.
Thanks
Get All Phone number from address book as formatted string and post this string to web-server. this make easy to sent all contact at small delay. at web-server side you can code to split phone number to separate number and check for user with same number.
-(NSString *)getPhoneContactAsSingleString
{
NSString *numberString=[NSString new];
numberString=#"";
CFErrorRef *error = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else { // we're on iOS 5 or older
accessGranted = YES;
}
if (accessGranted) {
#ifdef DEBUG
//NSLog(#"Fetching contact");
#endif
NSMutableArray *contacts=[[NSMutableArray alloc]init];
NSMutableDictionary *numberNameDictionary=[[NSMutableDictionary alloc]init];
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = CFArrayGetCount(allPeople);
for (int i = 0; i < nPeople; i++)
{
NSMutableDictionary *dictionary=[[NSMutableDictionary alloc]init];
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
if (person) {
NSString *firstname=[NSString new];
NSString *lastname =[NSString new];
CFStringRef firstnameStringRef=ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFStringRef lastnameStringRef=ABRecordCopyValue(person, kABPersonLastNameProperty);
if (firstnameStringRef) {
firstname=[NSString stringWithFormat:#"%#",firstnameStringRef];
}
if (lastnameStringRef) {
lastname =[NSString stringWithFormat:#"%#",lastnameStringRef];
}
NSString *name=[NSString new];
if (firstname.length!=0)
{
name=firstname;
if (lastname.length!=0) {
name=[name stringByAppendingString:#" "];
name=[name stringByAppendingString:lastname];
}
}
NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];
ABMultiValueRef multiPhones = ABRecordCopyValue(person, kABPersonPhoneProperty);
for(CFIndex i=0;i<ABMultiValueGetCount(multiPhones);i++) {
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(multiPhones, i);
NSString *phoneNumber = (__bridge NSString *) phoneNumberRef;
NSString *braketStripedNum = [phoneNumber stringByReplacingOccurrencesOfString:#"(" withString:#""];
braketStripedNum = [braketStripedNum stringByReplacingOccurrencesOfString:#")" withString:#""];
phoneNumber=braketStripedNum;
NSString *dashStripedNum = [self clean_phonenumber:phoneNumber with_string:#"-"];
phoneNumber=dashStripedNum;
NSString *comaStripedNum = [self clean_phonenumber:phoneNumber with_string:#","];
phoneNumber=comaStripedNum;
NSString *dotStripedNum = [self clean_phonenumber:phoneNumber with_string:#"."];
phoneNumber=dotStripedNum;
NSString *spaceStripedNum = [self clean_phonenumber:phoneNumber with_string:#" "];
phoneNumber=spaceStripedNum;
if (phoneNumber.length>10) {
phoneNumber=[phoneNumber substringFromIndex:phoneNumber.length-10];
}
if(phoneNumber.length!=0){
[phoneNumbers addObject:phoneNumber];
[numberNameDictionary setObject:name forKey:phoneNumber];
//NSLog(#"[%#] --> [%#]",orginal_phonenumber,phoneNumber);
numberString=[NSString stringWithFormat:#"%#%#,",numberString,phoneNumber];
}
}
//NSLog(#"%#",phoneNumbers);
if (phoneNumbers.count!=0) {
[dictionary setObject:name forKey:#"name"];
[dictionary setObject:phoneNumbers forKey:#"phonenumbers"];
[contacts addObject:dictionary];
}
}
}
if (numberString.length!=0) {
numberString=[numberString substringToIndex:[numberString length]-1];
}
}
else
{
#ifdef DEBUG
NSLog(#"Cannot fetch Contacts :( ");
#endif
}
return numberString;
}
Clean phone numbers
-(NSString *)clean_phonenumber:(NSString *)number with_string:(NSString *)string
{
NSArray *numberStrips = [number componentsSeparatedByString:string];
NSString *cleanNumber = #"";
for (NSString *eachString in numberStrips) {
cleanNumber = [NSString stringWithFormat:#"%#%#", cleanNumber, eachString];
}
return cleanNumber;
}
Just Check the method
NSLog(#"%#",[self getPhoneContactAsSingleString]);
Database (
{
to = (NSString *)
from = (NSString *)
subject = (NSString *)
uid = int
body = (NSString *)
}, { ...
)
Downloaded (
{
to = (NSString *)
from = (NSString *)
subject = (NSString *)
uid = int
body = (null)
}, { ...
)
I immediately pull and load an NSArray of about 200 NSDictionay objects from my Database into my UITableView, then I download an NSArray of the same structured NSDictionary but without a body.
Q: How do I go through all 200 Downloaded NSDictionary to see if it isn't already in my Database NSArray by matching the key: "uid"?
This should do the trick:
NSArray *arrayOfNew = [arrayDownload filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT (uid IN %#)", [arrayDataBase valueForKey:#"uid"]];
Tested with this sample data, if someone want to test it:
NSDictionary *dictionary0 = #{#"to":#"0",#"from":#"0",#"uid":#(0), #"body":#"0"};
NSDictionary *dictionary1 = #{#"to":#"1",#"from":#"1",#"uid":#(1), #"body":#"0"};
NSDictionary *dictionary2 = #{#"to":#"2",#"from":#"2",#"uid":#(2), #"body":#"0"};
NSDictionary *dictionary3 = #{#"to":#"3",#"from":#"3",#"uid":#(3), #"body":#"0"};
NSDictionary *dictionary4 = #{#"to":#"4",#"from":#"4",#"uid":#(2)};
NSDictionary *dictionary5 = #{#"to":#"5",#"from":#"5",#"uid":#(5)};
NSArray *arrayDataBase = #[dictionary0, dictionary1, dictionary2, dictionary3];
NSArray *arrayDownload = #[dictionary4, dictionary5];
//So the dictionary4 shouldn't be kept, and dictionary5 should be kept.
NSArray *arrayNew = [arrayDownload filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT (uid IN %#)", [arrayDataBase valueForKey:#"uid"]]];
NSLog(#"arrayNew: %#", arrayNew);
Output:
arrayNew: (
{
from = 5;
to = 5;
uid = 5;
}
With this code you can iterate in two arrays called "Downloaded" and "Database" and check if their uid match. I'm not sure if you're looking for a more elegant solution.
for (NSDictionary *dictDownloaded in Downloaded) {
for (NSDictionary *dictDatabase in Database) {
if ([dictDownloaded objectForKey:#"uid"] == [dictDatabase objectForKey:#"uid"]) {
NSLog(#"Object with uid: %d is in database", [[dictDownloaded objectForKey:#"uid"] intValue]);
}
}
}
Beware of nested loops :)
If you use Arturo's example (which works!) and download 1000 messages, you will have a potential of O(n*m) = 1000*200 = 200.000 "calculation steps"
Larme's attempt is pretty elegant (I like predicates!) but it's hard to predict the time it will use for execution, because it's all encapsulated within NSPredicate.
So another attempt, based on Larme's example data, would be to use a dictionary with the uid as the key for fast lookup.
NSMutableDictionary *databaseLookupDictionary = [[NSMutableDictionary alloc]init];
databaseLookupDictionary[#(0)] = #{#"to":#"0",#"from":#"0",#"uid":#(0), #"body":#"0"};
databaseLookupDictionary[#(1)] = #{#"to":#"1",#"from":#"1",#"uid":#(1), #"body":#"0"};
databaseLookupDictionary[#(2)] = #{#"to":#"2",#"from":#"2",#"uid":#(2), #"body":#"0"};
databaseLookupDictionary[#(3)] = #{#"to":#"3",#"from":#"3",#"uid":#(3), #"body":#"0"};
/* your download code */
// example data
NSMutableArray *downloadedData = [[NSMutableArray alloc]init];
[downloadedData addObject: #{#"to":#"0",#"from":#"0",#"uid":#(3)}];
[downloadedData addObject: #{#"to":#"0",#"from":#"0",#"uid":#(4)}];
for(NSDictionary *downloadDataDict in downloadedData)
{
// will be executed for message #4
if(![databaseLookupDictionary.allKeys containsObject:downloadDataDict[#"uid"]])
{
NSLog(#"Unknown message data found: %#", downloadDataDict);
}
}
This runs in linear time (O(n)*O(1)) so you should be fine with the performance. But keep in mind: if your message database count grows, you should think about searching directly in CoreData.
I'm working on iOS application with foursquare iOS api , I want to get the recommended near venues. I have used following code & it giving me an empty result .. Where have I done the mistake ? ? ?
NSArray* venues;
//get the foursquare locations
- (void)getTipsForLocation:(CLLocation *)location {
//NSLog(#"lat %f",location.coordinate.latitude);
[Foursquare2 venueExploreRecommendedNearByLatitude:#(location.coordinate.latitude)
longitude:#(location.coordinate.longitude)
near:nil
accuracyLL:nil
altitude:nil
accuracyAlt:nil
query:nil
limit:nil
offset:nil
radius:#(1500)
section:nil
novelty:nil
sortByDistance:1
openNow:0
venuePhotos:0
price:nil
callback:^(BOOL success, id result){
if (success) {
NSDictionary *dic = result;
venues = [dic valueForKeyPath:#"response.venues"];
FSConverter *converter = [[FSConverter alloc]init];
self.nearbyVenues = [converter convertToObjects:venues];
//NSLog(#"venues %#",venues);
//NSLog(#"near by places %#",self.nearbyVenues);
}
else{
NSLog(#" foursquare connecting error");
}
}];
NSLog(#"recommended place array %#",venues);
}
You Can Not pass Nil,In NSNumber & NSString.
NSNumber *emptynumber=[[NSNumber alloc] init];
[Foursquare2 venueExploreRecommendedNearByLatitude:lan longitude:lon near:#"" accuracyLL:emptynumber altitude:emptynumber accuracyAlt:emptynumber query:#"" limit:emptynumber offset:emptynumber radius:#(1500) section:#"" novelty:#"" sortByDistance:YES openNow:YES venuePhotos:YES price:#"" callback:^(BOOL success, id result) {
if (success) {
NSLog(#"secondResult: %#",result);
NSDictionary *dic = result;
NSArray *venues = [dic valueForKeyPath:#"response.venues"];
FSConverter *converter = [[FSConverter alloc] init];
self.venues = [converter convertToObjects:venues];
[self.tableView reloadData];
NSLog(#"Data: %#",venues);
}
}];
It Works For me.
I am using MapKit to do a local search which returns one or more MKMapItem objects. But there are members of the object I can see in the debugger but I can't access. The one I particularly need is UID.
I have tried item.placemark, but it does not let me access the UID. This seems like it should be really simple. What am I missing here?
This does not work:
NSString *uid = item.placemark.UID
This does not work:
NSDictionary *mapItemDictionary = (NSDictionary *)item;
NSString *uid = [mapItemDictionary objectForKey:#"UID"];
But the debugger command po item shows me all the members of the object:
Name: Shell CurrentLocation: 0 Place: <GEOPlace: 0x17014e650>
{
address = {
business = (
{
**UID = 2478578482074921045**;
URL = "www.shell.com";
canBeCorrectedByBusinessOwner = 1;
name = Shell;
source = (
{
"source_id" = A3H0281540;
"source_name" = "acxiom_us";
},
{
"source_id" = 2276257;
"source_name" = localeze;
}
);
telephone = "+14803968213";
}
);
Any help with this would be appreciated. Here is the code I'm using:
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error)
{
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop)
{
MKPlacemark *placemark = (MKPlacemark *)item.placemark;
NSDictionary *addressDict = placemark.addressDictionary;
NSArray *businessArray = addressDict[#"business"];// businessArray is NIL
NSString *uid=nil;
if (businessArray != nil && businessArray.count >0) {
NSDictionary *businessDict=businessArray[0];
uid=businessDict[#"UID"];
}
NSLog(#"UID is %#",uid);
}];
Ok, so after a lot of digging it seems that the information is in a couple of private objects. The "place" property is a GEOPlace, and this has a property, business, which is an array that contains a GEOBusiness object. Since this is private data you cannot access it directly via properties, but you can get it via key-value encoding. The following code extracts the UID -
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop) {
NSValue *place = [item valueForKey:#"place"];
NSArray *businessArray = (NSArray *)[place valueForKey:#"business"];
NSNumber *uid=nil;
if (businessArray != nil && businessArray.count >0) {
id geobusiness=businessArray[0];
uid=[geobusiness valueForKey:#"uID"];
}
NSLog(#"UID is %#",[uid stringValue]);
}];
As this is private data structures there is no guarantee that it won't change. I am also unsure whether the App store validation process will flag this as private api access - Since it is using valueForKey I don't think it will, but there are no guarantees.