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
}
Related
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.
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.
In an iOS App (Objective-C) I send messages with video and the user can select if it is private, for friends, or visible to the public.
That worked fine for a long time but I recently discovered that messages that were sent with "audience = ACFacebookAudienceOnlyMe" are also visible to the public. Posting with "audience = ACFacebookAudienceFriends" is still working as expected.
My codes hasn't changed and I don't know if this behavior appeared in iOS9 or if it is a problem with Facebook.
Does anybody has noticed a similar behavior?
The relevant code is:
NSString * audience;
switch ([[messageOptions objectForKey:#"permission"]intValue]) {
case 0:
audience = ACFacebookAudienceOnlyMe;
break;
case 1:
audience = ACFacebookAudienceFriends;
break;
case 2:
audience = ACFacebookAudienceEveryone;
break;
default:
audience = ACFacebookAudienceOnlyMe;
break;
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"XXXXMyAppIDKeyXXXX", ACFacebookAppIdKey, permissions, ACFacebookPermissionsKey, audience, ACFacebookAudienceKey, nil];
__block ACAccount * facebookAccount;
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:dict completion:^(BOOL granted, NSError *error) {
if (granted && error == nil) {... send message and video here..}
The Dictionary "dict" looks like this when printed with NSLog directly before sending:
{
ACFacebookAppIdKey = XXXXMyAppIDKeyXXXX;
ACFacebookAudienceKey = me;
ACFacebookPermissionsKey = (
"publish_actions"
);
}
When Posting for friends ACFacebookAudienceKey is "friends".
It seems that it was a temporary problem, after some weeks I can not reproduce the behavior.
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
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.