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

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.

Related

IOS Objective-c - Getting contacts not working - No request prompt - Access denied

I am using Objective-c to develop an app for iPad. I need to fetch the address book for the contacts. But I get no access request prompt and the access stays denied. The boolean "granted" is never true and the code to get the contacts array is never reached. Therefore the contacts array contactsArray stays empty.
Following is the code I am using:
-(void) fetchAllContacts
{
contactsArray = [[NSMutableArray alloc] init];
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType : CNEntityTypeContacts completionHandler : ^(BOOL granted, NSError * _Nullable error)
{
if (granted)
{
// Code to get the contacts array
// contactsArray = ....
}
}];
}
Any help?
Thank you
iOS will only present the modal access request prompt once. If you have denied the access the first time, the app will be unable to access it until the user changes the app's permissions in the iOS settings.
One option is to present a custom prompt saying access is denied with a button to navigate directly to the app settings page, using UIApplicationOpenSettingsURLString as an URL.
//objc
NSURL * url = [[NSURL alloc] initWithString:UIApplicationOpenSettingsURLString];
[UIApplication.sharedApplication openURL:url];
//swift
if let url = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared.openURL(url)
}

Can't get Contact Groups to show up in 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.

create new group with contacts framework, CNErrorDomain Code = 2

i try to create and save a group with the Contacts Framework.
First the user authorize the App for contacts access.
A viewcontroller is presented and with a + button user shows an alertview with textfield.
The user types the group name he wants and click to button of the alertview (save).
This is the code for saving the new group. The group name is available but it is not possible to save this group anyway:
CNContactStore *contactStore = [CNContactStore new];
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError *error){
if (granted) {
CNMutableGroup *newGroup = [CNMutableGroup new];
CNSaveRequest *saveRequest = [CNSaveRequest new];
[newGroup setName:groupName];
//when saving to container with identifier nil, we get this error:
//Error Domain=CNErrorDomain Code=2 "(null)" UserInfo={CNInvalidRecords=(
//"<CNMutableGroup: 0x10a059f20: identifier=2F4981B9-8A47-45A4-8841-1FA5A09584A4:ABGroup, name=gghh>"
[saveRequest addGroup:newGroup toContainerWithIdentifier:nil];
[contactStore executeSaveRequest:saveRequest error:&error];
if (error){
//error saving group
//NSLog(#"error message: %#",error);
} else {
//if no errors, reload tableview
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
}];
Error Domain=CNErrorDomain Code=2 "(null)" UserInfo={CNInvalidRecords=(
"<CNMutableGroup: 0x14fb3e5e0: identifier=8E490585-1223-407E-B353-0D25609B05AB:ABGroup, name=jddjd>"
)}
The next strange thing is: why is the save request trying to save this group
with identifier :ABGroup at the end?
The Error contains a info about CNInvalidRecords.
I am only using the Contacts Framework.
Why is this happening?
Any solutions for that?
It worked fine for me, with essentially the same code.
CNMutableGroup *newGroup = [CNMutableGroup new];
CNSaveRequest *saveRequest = [CNSaveRequest new];
[newGroup setName:self.groupName];
[saveRequest addGroup:newGroup toContainerWithIdentifier:nil];
[contactStore executeSaveRequest:saveRequest error:&error];
And created a new group

Issue about the newest Contacts ios Framework

I'm working on my app and at a certain point the user can convince their friends to download it. However, the ABAddressBook framework (link) has been deprecated with iOS 9, so I had to teach myself the newest Contacts framework (link).
However, I'm still facing issues. I have read the documentation up to this point:
NSArray *keysToFetch = #[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
NSString *containerId = [self.CN_contacts defaultContainerIdentifier];
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
self.allContacts = [self.CN_contacts unifiedContactsMatchingPredicate:predicate keysToFetch:keysToFetch error:nil];
But I know that the block of code is missing the functionality of asking the user to grant access to their contacts.
Does anyone knows a way to ask user with CNAuthorizationStatus?
You have to use it like this
switch CNContactStore.authorizationStatusForEntityType(CNEntityType.Contacts)
{
case CNAuthorizationStatus.Denied,CNAuthorizationStatus.Restricted :
//Handle denied and restricted case
break
case CNAuthorizationStatus.NotDetermined :
//Request Access
contactsStore?.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (granted, error) -> Void in
//At this point an alert is provided to the user to provide access to contacts.
//This will get invoked if a user responds to the alert
if (!granted ){
//User has allowed the access in the alertview provided
}
else{
//User has decline the access in the alertview provided
}
})
break
case CNAuthorizationStatus.Authorized :
//Do your stuff
NSArray *keysToFetch = #[CNContactGivenNameKey,CNContactFamilyNameKey, CNContactPhoneNumbersKey];
NSString *containerId = [self.CN_contacts defaultContainerIdentifier];
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
self.allContacts = [self.CN_contacts unifiedContactsMatchingPredicate:predicate keysToFetch:keysToFetch error:nil];
break
}

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.

Resources