create new group with contacts framework, CNErrorDomain Code = 2 - ios

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

Related

"The user is blocked from live streaming." (Domain=com.google.GTLRErrorObjectDomain Code=403)

When I'm trying to perform request for inserting of a broadcast I receive error:
Error Domain=com.google.GTLRErrorObjectDomain Code=403 "The user is blocked from live streaming."
UserInfo={GTLRStructuredError=GTLRErrorObject 0x28027ad30: {code:403
errors:[1] message:"The user is blocked from live streaming."},
NSLocalizedDescription=The user is blocked from live streaming.}
I have started receiving this error today. Before, everything has been working fine. I have tested on several accounts and had not any luck.
Code:
GTLRYouTube_LiveBroadcastSnippet *broadcastSnippet= [[GTLRYouTube_LiveBroadcastSnippet alloc] init];
[broadcastSnippet setTitle:title];
[broadcastSnippet setScheduledStartTime:[GTLRDateTime dateTimeWithDate:self.beginOfStream]]; // current date + 1 minute.
[broadcastSnippet setScheduledEndTime:[GTLRDateTime dateTimeWithDate:[NSDate dateWithTimeIntervalSinceNow:80000]]];
GTLRYouTube_LiveBroadcastStatus *status = [[GTLRYouTube_LiveBroadcastStatus alloc] init];
[status setPrivacyStatus:[StreamSettings youtubeStringForPrivacyStatus:[privacyStatus intValue]]];
GTLRYouTube_LiveBroadcastContentDetails *details = [self streamDetailsWith:latency];
GTLRYouTube_LiveBroadcast *broadcast = [[GTLRYouTube_LiveBroadcast alloc] init];
[broadcast setKind:#"youtube#liveBroadcast"];
[broadcast setSnippet:broadcastSnippet];
[broadcast setStatus:status];
GTLRYouTubeQuery_LiveBroadcastsInsert *query = [GTLRYouTubeQuery_LiveBroadcastsInsert queryWithObject:broadcast
part:#"id, snippet, contentDetails,status"];
GTLRYouTubeService *service = self.youTubeService;
__strong id <YouTubeHelperDelegate> strongDelegate = self.delegate;
[service executeQuery:query completionHandler:^(GTLRServiceTicket *ticket,
GTLRYouTube_LiveBroadcast *returnedBrocast,
NSError *error) {
if (error) {
NSLog(#"%#", error); //Here is place I got an error
}
}];
If the logged in user does not have more than 1000 subscribers he can't go live using a mobile app, as mentioned in link:
https://support.google.com/youtube/answer/2853834?hl=en

how to check No Fingerprints added for Touch ID in iOS

I am integrating Touch ID access in one of my app. I have successfully integrated it. Here is that code:
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.75 * NSEC_PER_SEC), highPriorityQueue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
LAContext *context = [[LAContext alloc] init];
isTouchExists = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
if (isTouchExists) {
NSString * keychainItemIdentifier;
NSString * keychainItemServiceName;
keychainItemIdentifier = #"fingerprintKeychainEntry";
keychainItemServiceName = [[NSBundle mainBundle] bundleIdentifier];
NSData * pwData = [#"the password itself does not matter" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary * attributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
(__bridge id)(kSecClassGenericPassword), kSecClass,
keychainItemIdentifier, kSecAttrAccount,
keychainItemServiceName, kSecAttrService, nil];
CFErrorRef accessControlError = NULL;
SecAccessControlRef accessControlRef = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAccessControlUserPresence,
&accessControlError);
if (accessControlRef == NULL || accessControlError != NULL)
{
NSLog(#"Cannot create SecAccessControlRef to store a password with identifier “%#” in the key chain: %#.", keychainItemIdentifier, accessControlError);
}
attributes[(__bridge id)kSecAttrAccessControl] = (__bridge id)accessControlRef;
attributes[(__bridge id)kSecUseNoAuthenticationUI] = #YES;
attributes[(__bridge id)kSecValueData] = pwData;
CFTypeRef result;
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)attributes, &result);
if (osStatus != noErr)
{
NSError * error = [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
NSLog(#"Adding generic password with identifier “%#” to keychain failed with OSError %d: %#.", keychainItemIdentifier, (int)osStatus, error);
}
//other my code for success
}
});
});
Now, If I remove all the fingerprints from settings in iPhone, This code will work and ask for passcode. So My question is:
how can I come to know that there is no any fingerprints added for Touch ID?
I don't want to show iOS device passcode screen as I have already built passcode screen for my app security. So is there any option to check device have atleast one fingerprint available for Touch ID access?
Thanks in advance.
======== EDIT 1 ===========
It is working on my side also. The issue is I need to check it each time when I am asking for Touch ID. I need to fetch status in viewWillAppear or in applicationDidBecomeActive each time whenever I want to use Touch ID access in app, as I am removing fingers run time, it may not reflecting in my code so I need to fetch each time.
canEvaluatePolicy:error: will be Error : LAErrorTouchIDNotEnrolled
Authentication could not start because Touch ID has no enrolled
fingers.
APPLE DOC Ref.
Try:
LAContext *context = [[LAContext alloc] init];
NSError *error;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"My Reason" reply:^(BOOL success, NSError * _Nullable error) {
}];
}else{
if (error.code == LAErrorTouchIDNotEnrolled) {
NSLog(#"Error: %#", error.localizedDescription);
}
}
If there are no fingerprints registered, canEvaluatePolicy should return false.
Source : https://developer.apple.com/documentation/localauthentication/lacontext/1514149-canevaluatepolicy?language=objc

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.

download-url of GTLDriveFile coming nil and even GTLDriveFileExportLinks coming nil

I want to download the files from google-drive. I have fetched the GTLDriveFile objects from a drive. But downloadUrl property of these file objects are nil.
Googling more, i got that file.exportLinks also has download links. But that is also coming nil.
I used this code to fetch files:
- (void)fetchFiles {
GTLQueryDrive *query =
[GTLQueryDrive queryForFilesList];
query.maxResults = 10;
[self.service executeQuery:query
delegate:self
didFinishSelector:#selector(displayResultWithTicket:finishedWithObject:error:)];
}
- (void)displayResultWithTicket:(GTLServiceTicket *)ticket
finishedWithObject:(GTLDriveFileList *)files
error:(NSError *)error {
if (error == nil) {
if (files.items.count > 0) {
fileArr=files.items;
}
}
}
here fileArr has the files of class GTLFileDrive.
From Google-drive developer site, i got the following code snippet to download file using a parameter Url:
GTMHTTPFetcher *fetcher = [self.service.fetcherService fetcherWithURLString: urlOfFile];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
if (error == nil) {
NSLog(#"Retrieved file content");
// Do something with data
} else {
NSLog(#"An error occurred: %#", error);
}
}];
I need the urlOfFile to complete the task.
Any help would be greatly appreciated.
When you do your initial auth, you need to request the right scope - otherwise, you only get rights to list the files, and not download them.
Something like.
NSArray *scopes = [NSArray arrayWithObjects:kGTLAuthScopeDrive, nil];
authController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:[scopes componentsJoinedByString:#" "]
clientID:self.clientId
clientSecret:self.clientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
You can see this all in my new Google Drive Picker
https://github.com/ConfusedVorlon/HSGoogleDrivePicker

CloudKit: creatorUserRecordID of CKRecord Issue (or Bug?)

After iOS 8.3, if the record is created by the current account, its creatorUserRecordID will be like
CKRecordID: [Some Address]; defaultOwner:(_defaultZone:defaultOwner)
And then if fetch this recordID using fetchRecordWithID:completionHandler: from CKDatabase, it will always return error like
CKError [Some Address]: "Unknown Item" (11/2003); server message = "Record not found"; uuid = [Some UUID]; container ID = [Some Container ID]
I never encounter this issue before.
Is it a bug, or should I fetch record from recordID like this ( defaultOwner ) in other way?
EDIT (add sample code)
- (void)postMoodFeed:(NSString *)moodFeed
{
CKRecord *moodRecord = [[CKRecord alloc] initWithRecordType:#"Mood"];
moodRecord[#"moodFeed"] = moodFeed
[[[CKContainer defaultContainer] publicCloudDatabase] saveRecord:moodRecord completionHandler:^(CKRecord *record, NSError *error) {
[self queryMyMood];
}];
}
- (void)queryMyMood
{
// currentUserRecordID is fetched from fetchUserRecordIDWithCompletionHandler: of CKContainer
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"creatorUserRecordID = %#", currentUserRecordID];
CKQuery *query = [[CKQuery alloc] initWithRecordType:#"Mood" predicate:predicate];
[[[CKContainer defaultContainer] publicCloudDatabase] performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
if (results) {
for (CKRecord *eachRecord in results) {
// Following logs are all __defaultOwner__
NSLog(#"%#", eachRecord.creatorUserRecordID.recordName);
[[[CKContainer defaultContainer] publicCloudDatabase]fetchRecordWithID:eachRecord.creatorUserRecordID completionHandler:^(CKRecord *record, NSError *error) {
// All following logs are "Unknown item" error
NSLog(#"%#", error);
}];
}
}
}];
}
EDIT July 2, 2015
That is a bug.
After reporting to Apple, they fixed this issue in iOS 9 Beta 2.
Indeed it looks like new functionality.
What you could do is first testing if the eachRecord.creatorUserRecordID.recordName == "defaultOwner" and if that's the case you could fetch the record for the ID that you got from the currentUserRecordID
But it would be better to not use the creatorUserRecordID for any functionality in your app. You could better add a new CKReference field and always fill it with the currentUserRecordID. Then even if you have a process that migrates data, you would still know who created that record originally.
Maybe this will help: I've found that if you use [CKFetchRecordsOperation fetchCurrentUserRecordOperation] at the beginning of your app's workflow you won't have defaultOwner CKRecordIDs come out of nowhere.

Resources