Can't get Contact Groups to show up in iOS - ios

No matter what I try, iOS is telling me that I have 0 groups. I've tried both the ABGroup and CNGroup API. Nada. If I go into the Contacts app on my iPad and click on Groups, I see it listing five different groups (as expected). But both [CNContactStore groupsMatchingPredicate:error:] and ABAddressBookCopyArrayOfAllGroups() are giving me 0 element arrays back.
Here is my code for each:
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted == YES) {
NSError *error;
NSArray *cnGroups = [store groupsMatchingPredicate:nil error:&error];
for( CNGroup *group in cnGroups ) {
AWLogInfo(#"JMS CN Group: %#", group.name);
}
}
}];
CFArrayRef allGroups = ABAddressBookCopyArrayOfAllGroups(addressBook);
for( int iGroup = 0; iGroup < CFArrayGetCount(allGroups); iGroup++ ) {
ABRecordRef groupRef = CFArrayGetValueAtIndex(allGroups, iGroup);
NSString *groupName = (__bridge_transfer NSString*)ABRecordCopyValue(groupRef, kABGroupNameProperty);
AWLogInfo(#"JMS Group %ld: %#", (long) iGroup, groupName);
}
(Note that I don't request AddressBook permission, because that's already being done elsewhere in my app. I am able to access contacts just fine -- in fact, right after this code snippet is some code that iterates through all of the contact records and it is no problem.)
What gives? What am I missing?
Edit: Note that I can see groups I create, I just can't see the groups created by other apps. I can see the contacts within those groups, but not the groups themselves. Is this expected behavior?

I was confusing groups with sources. If you list all sources, you'll get the stuff created by other apps.
That said, iOS seems to be pretty lousy about giving you a useful name for a source, so this still isn't really doing what I want.

Related

How can I determine that CNContact is is favorites? [duplicate]

This question already has answers here:
Find Favorite contacts from the iOS Address Book API
(3 answers)
Closed 4 years ago.
My iOS application fetches contacts from device using code
[[CNContactStore new] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError *error) {
if (granted) {
NSArray *keys = #[CNContactNamePrefixKey,
CNContactGivenNameKey,
CNContactMiddleNameKey,
CNContactFamilyNameKey,
// ...
NSString *containerId = store.defaultContainerIdentifier;
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
for (CNContact *contact in cnContacts) {
// contacts fetching
}
}
}];
Has CNContact class some value means that contact is in favorites on device (in Phone application)? I didn't found such key in CNContact keys.
Or maybe predicate contains keys that I needs?
Filip Radelic answered same question:
Favorites are stored inside Phone.app, not inside the Address Book database itself. You can't access other app's sandbox on a non-jailbroken iPhone, so unfortunately the answer is no.
Also, it would be pretty bad for privacy if any app could see your favorite contacts. It's already bad that it can access entire address book without asking you.

Saving an image as Contact Picture, and displaying it while incoming call

Requirement: I am saving some contacts into the user's iPhone along with a picture (dimensions same as the device). I want this picture to be displayed ON FULLSCREEN whenever the contact calls on that device.
Noticed Example: Truecaller iOS app shows as Red image when the caller is Identified as Spam
Code: This is code I have used to save the contacts data. I am using Contacts.framework
CNMutableContact *newContact = [CNMutableContact new];
newContact.imageData = UIImagePNGRepresentation([UIImage imageNamed:#"blue_bg.png"]);
newContact.contactType = CNContactTypePerson;
newContact.givenName = user.firstName;
newContact.middleName = user.middleName;
newContact.familyName = user.lastName;
NSArray *numbers = [[NSArray alloc] initWithArray:#[[CNLabeledValue labeledValueWithLabel:#"Main" value:[CNPhoneNumber phoneNumberWithStringValue:user.mobileNumber.stringValue]]]];
newContact.phoneNumbers = numbers;
CNContactStore *store = [CNContactStore new];
CNSaveRequest *saveReq = [CNSaveRequest new];
[saveReq addContact:newContact toContainerWithIdentifier:nil];
NSError *error = nil;
[store executeSaveRequest:saveReq error:&error];
if (error) {
NSLog(#"Contact Save ERROR: %#", error.localizedDescription);
}
Current Scenario: I am getting this image in the iOS Contacts App but its not displayed when that user calls on the iPhone. How does Truecaller do it? What am I missing here?
If the image shows up in the Contacts App it should show up when you're getting called by that person.

Deleting and recreating EKCalendar

In my app, I create events in an EKCalendar. I fetch the events online, and in order to refresh the events, I want to first delete the calendar (if it exists), recreate it, and then put the new events in there.
To instantiate the calendar I use
- (EKCalendar *)calendar {
if (!_calendar) {
NSArray *calendars = [self.store calendarsForEntityType:EKEntityTypeEvent];
NSString *calendarTitle = #"MyCalendar";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title matches %#", calendarTitle];
NSArray *filtered = [calendars filteredArrayUsingPredicate:predicate];
if ([filtered count]) {
_calendar = [filtered firstObject];
} else {
_calendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:self.store];
_calendar.title = calendarTitle;
_calendar.source = self.store.defaultCalendarForNewEvents.source;
NSError *calendarErr = nil;
BOOL calendarSuccess = [self.store saveCalendar:_calendar commit:YES error:&calendarErr];
if (!calendarSuccess) {
NSLog(#"Calendar Error = %#", [calendarErr localizedDescription]);
}
}
}
return _calendar;
}
To delete the calendar, I use
-(IBAction)deleteCalendar{
NSError *error = nil;
[self.store removeCalendar:_calendar commit:YES error:&error];
}
Both methods work fine individually.
So, when I start the creation of events, I do the following:
[self deleteCalendar];//delete calendar and its events, in case it already exists
[self calendar];//create calendar
[self importEvents];//put events in calendar
Now, what I observe is the following:
On the first run of the app
a calendar is created
events are imported. (This is expected, and works just fine)
While the app is running, I trigger the above methods again with a button. With the following, for me puzzling, result:
the calendar is deleted (expected result)
NO calendar is created (WHY? that is my main question).The "if (!_calendar)" part of the method is considered FALSE, and nothing is executed.
The 'importEvents' method runs through its regular hoopla, without any apparent errors, although I would expect something like a 'no source' error.
Please advise.
UPDATE:
This could be an indicator of what is happening, but I still don't get it:
After a while, the events appear in a different calendar, i.e. not the calendar called 'myCalendar', but another, iCloud based calendar, apparently the one that at that point is the defaultCalendarForNewEvents. However, that also doesn't make any sense to me.
OK, so, what is happening:
I have deleted the Calendar from the store, effectively, but a reference to that calendar actually was still hanging around in my app.
I solved it as follows:
-(IBAction)deleteCalendar:(id)sender{
NSError *error = nil;
if(_calendar){
[self.store removeCalendar:_calendar commit:YES error:&error];
}
_calendar = nil;
}
I hope this is useful to someone

Exception when trying to convert HeartRate from HealthKitStore

I am working on my first iPhone App: a simple app showing the heartRate results from HealthKit in a nice way. My first step is to show the results as a raw text. But unfortunately I'm getting an exception at the following line, telling me: "thread 1 signal SIGABRT". Does someone know, what I did wrong and hint me in a direction?
double usersBeatsPerMinute = [quantity doubleValueForUnit:[HKUnit countUnit]];
The rest of the code looks like this:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// Set up an HKHealthStore, asking the user for read/write permissions. The profile view controller is the
// first view controller that's shown to the user, so we'll ask for all of the desired HealthKit permissions now.
// In your own app, you should consider requesting permissions the first time a user wants to interact with
// HealthKit data.
if ([HKHealthStore isHealthDataAvailable]) {
NSSet *writeDataTypes = [self dataTypesToWrite];
NSSet *readDataTypes = [self dataTypesToRead];
[self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {
if (!success) {
NSLog(#"You didn't allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: %#. If you're using a simulator, try it on a device.", error);
return;
}
}];
}
HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
// Since we are interested in retrieving the user's latest sample
// we sort the samples in descending order by end date
// and set the limit to 1
// We are not filtering the data, and so the predicate is set to nil.
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
// construct the query & since we are not filtering the data the predicate is set to nil
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:weightType predicate:nil limit:1 sortDescriptors:#[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
// if there is a data point, dispatch to the main queue
if (results) {
dispatch_async(dispatch_get_main_queue(), ^{
HKQuantitySample *quantitySample = results.firstObject;
// pull out the quantity from the sample
HKQuantity *quantity = quantitySample.quantity;
double usersBeatsPerMinute = [quantity doubleValueForUnit:[HKUnit countUnit]];
_HeartRateResults.text = [NSString stringWithFormat:#"%# lbs", [NSNumberFormatter localizedStringFromNumber:#(usersBeatsPerMinute) numberStyle:NSNumberFormatterNoStyle]];
});
}
}];
// do not forget to execute the query after its constructed
[_healthStore executeQuery:query];}
There was a comment in the documentation ("These samples use count/time units") I didn't quite understand, so I did a little searching and tried it out and was able to get a value I manually put into the Health app using this:
double rate = [mostRecentQuantity doubleValueForUnit:[[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]];
I haven't seen unitDividedByUnit before. Here's the article I pulled it from.

Sorting contacts based on recently used/most used/urgency in AddressBook iOS

I need to sort contacts in user's device based on recently used/most used/urgency and display them in a single view(not as in the phone app where these are displayed in separate tabs). I have researched about this but only got to know about sorting contacts alphabetically. Does the AddressBook framework allow this or is there some other way around? Any help would be appreciated.
No. Unfortunately, there is no way of knowing if a record has been recently used or not.
However, you can sort records in the address book using the properties specified below:
Here is a simple example of sorting records based on the creation date in descending order.
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(nil, NULL);
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBookRef));
NSArray *sortedPeople = [allPeople sortedArrayUsingComparator:^NSComparisonResult(id aRecord, id otherRecord) {
ABRecordRef aRecordRef = (__bridge ABRecordRef)aRecord;
ABRecordRef otherRecordRef = (__bridge ABRecordRef)otherRecord;
NSDate *aRecordCreationDate = CFBridgingRelease(ABRecordCopyValue(aRecordRef, kABPersonCreationDateProperty));
NSDate *otherRecordCreationDate = CFBridgingRelease(ABRecordCopyValue(otherRecordRef, kABPersonCreationDateProperty));
return [aRecordCreationDate compare:otherRecordCreationDate];
}];
});
CFRelease(addressBookRef);
Hi I have found a solution First we create Abaddressbook and register it with changes callback like this
1)ABAddressBookRef ntificationaddressbook = ABAddressBookCreate();
2)ABAddressBookRegisterExternalChangeCallback(ntificationaddressbook,addressBookChanged, self);
and when you will make a change in any contact (modified,open,edit etc).
then its callback will call its delegate like this
3)void addressBookChanged(ABAddressBookRef reference,
CFDictionaryRef dictionary,
void *context)
{
/// Address book is changed
// Now you can Implement logic
1-First of all get the change date and time which is now. from [NSdate date].
this will be the change date/time of a record
}
Hope it will help you.

Resources