I wrote the code below to share a CKRecord:
CKRecordZone *restaurantsZone = [[CKRecordZone alloc] initWithZoneName:#"RestaurantsZone"];
CKRecordID *recordID = [[CKRecordID alloc] initWithRecordName:self.recordName zoneID:restaurantsZone.zoneID];
CKRecord *record = [[CKRecord alloc] initWithRecordType:#"Restaurant" recordID:recordID];
[record setValue:self.restaurant forKey:#"name"];
UICloudSharingController *cloudSharingController = [[UICloudSharingController alloc] initWithPreparationHandler:^(UICloudSharingController * _Nonnull controller, void (^ _Nonnull preparationCompletionHandler)(CKShare * _Nullable, CKContainer * _Nullable, NSError * _Nullable)) {
[self shareRootRecord:record name:self.restaurant completion:preparationCompletionHandler];
}];
cloudSharingController.delegate = self;
[self presentViewController:cloudSharingController animated:YES completion:nil];
And the shareRootRecord function:
- (void)shareRootRecord:(CKRecord *)rootRecord name:(NSString *)name completion:(void (^)(CKShare * _Nullable share, CKContainer * _Nullable container, NSError * _Nullable error))completion
{
CKShare *shareRecord = [[CKShare alloc] initWithRootRecord:rootRecord];
shareRecord[CKShareTitleKey] = name;
NSArray *recordsToSave = #[rootRecord, shareRecord];
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *privateDatabase = [container sharedCloudDatabase];
CKModifyRecordsOperation *operation = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:recordsToSave recordIDsToDelete:#[]];
[operation setPerRecordCompletionBlock:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if (error) {
NSLog(#"%#", [error localizedDescription]);
}
}];
[operation setModifyRecordsCompletionBlock:^(NSArray<CKRecord *> * _Nullable savedRecords, NSArray<CKRecordID *> * _Nullable deletedRecordIDs, NSError * _Nullable error) {
if (error) {
NSLog(#"%#", [error localizedDescription]);
}
completion(shareRecord, container, error);
}];
[privateDatabase addOperation:operation];
}
Now, when I run this code, the following error is thrown: Only shared zones can be accessed in the shared DB. I can't seem to be able to figure out why, though. Any ideas?
Make sure the CKRecord you want to share is already in the owners privateDB before you share it.
When a participant accepts the share, that's when the record will appear in the participants sharedDB.
This code creates a record and a share and then tries to modify the sharedDB of the owner with the records.
Conceptually, you want to share a a record in an owners privateDB with a participant. The sharedDB of a participant acts as a window into the privateDB of the owner.
Related
I am trying to read user's name from their iCloud account using CloudKit as below
CKContainer *container = [CKContainer defaultContainer];
[container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
if (accountStatus == CKAccountStatusAvailable) {
[container requestApplicationPermission:CKApplicationPermissionUserDiscoverability completionHandler:^(CKApplicationPermissionStatus applicationPermissionStatus, NSError * _Nullable error) {
if (applicationPermissionStatus == CKApplicationPermissionStatusGranted) {
[container fetchUserRecordIDWithCompletionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
if (!error && recordID) {
[container discoverUserIdentityWithUserRecordID:recordID completionHandler:^(CKUserIdentity * _Nullable userInfo, NSError * _Nullable error) {
NSLog(#"UserInfo: %# Error: %#", userInfo, error);
}];
}
}];
}
}];
}
}];
but every time requestApplicationPermission fails with Service Unavailable error and status is CKApplicationPermissionStatusCouldNotComplete. I am not sure what am I doing wrong here. I tried it with multiple devices and accounts but still result is same. I have tried disabling and enabling CloudKit in capabilities as was suggested in some places but no luck. Is there any information that I need to configure which I am missing. Below is how my iCloud capability looks
Well it turns out I needed to call statusForApplicationPermission: before calling requestApplicationPermission. It was really baffling that just because I did not check the permission status, the permission request was failing. That too with Service UnAvailable error. Here is the code that works(not very elegant):
CKContainer *container = [CKContainer defaultContainer];
[container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
if (accountStatus == CKAccountStatusAvailable) {
[container statusForApplicationPermission:CKApplicationPermissionUserDiscoverability completionHandler:^(CKApplicationPermissionStatus applicationPermissionStatus, NSError * _Nullable error) {
if (applicationPermissionStatus==CKApplicationPermissionStatusInitialState || applicationPermissionStatus==CKApplicationPermissionStatusGranted) {
[container requestApplicationPermission:CKApplicationPermissionUserDiscoverability completionHandler:^(CKApplicationPermissionStatus applicationPermissionStatus, NSError * _Nullable error) {
if (applicationPermissionStatus == CKApplicationPermissionStatusGranted) {
[container fetchUserRecordIDWithCompletionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
if (!error && recordID) {
[container discoverUserIdentityWithUserRecordID:recordID completionHandler:^(CKUserIdentity * _Nullable userInfo, NSError * _Nullable error) {
NSLog(#"UserInfo: %# Error: %#", userInfo, error);
}];
}
}];
}
}];
}
}];
}
}];
I am using QuickBlox successfully in a a main iOS app and would like to extend the features in to a Share Extension using the cocoapod 'QuickBlox', '~> 2.7.5'. I needs to send the message in the background without opening the main application.
I am using the code below without any success to first setup sending text only.
[QBRequest logInWithUserLogin:USERNAME password:PASSWORD successBlock:^(QBResponse *response, QBUUser *user) {
if (user) {
user.login = USERNAME;
user.password = PASSWORD;
[[QBChat instance] connectWithUser:user completion:^(NSError * _Nullable error) {
QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:[settingsDict valueForKey:#"sendingID"] type:QBChatDialogTypeGroup];
QBChatMessage *messagetosend = [QBChatMessage message];
messagetosend.senderID = userQBID;
messagetosend.text = self.contentText;
messagetosend.dateSent = [NSDate dateWithTimeInterval:-12.0f sinceDate:[NSDate date]];
[chatDialog joinWithCompletionBlock:^(NSError * _Nullable error) {
[chatDialog sendMessage:messagetosend completionBlock:^(NSError * _Nullable error) {
NSLog(#"%#",[error localizedDescription]);
}];
}];
}
];
}
} errorBlock:^(QBResponse * _Nonnull response) { }];
You can send the message via REST. Sending via REST doesn't need to connect to chat and join in the dialog.
NSUInteger senderID = //Current User ID
QBChatMessage *message = [QBChatMessage message];
message.text = intent.content;
message.senderID = senderID;
message.markable = YES;
message.deliveredIDs = #[#(senderID)];
message.readIDs = #[#(senderID)];
message.dialogID = dialogID;
message.dateSent = [NSDate date];
[QBRequest sendMessage:message successBlock:^(QBResponse * _Nonnull response, QBChatMessage * _Nonnull createdMessage) {
} errorBlock:^(QBResponse * _Nonnull response) {
}];
I am using braintree ios sdk and using their Drop in UI to add a customer card details. i am getting the UI successfully and after the customer submit the information, how can i get the paymentMEthodNounce from the result object. here is my code.
- (void)showDropIn:(NSString *)clientTokenOrTokenizationKey {
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientTokenOrTokenizationKey request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else if (result.cancelled) {
NSLog(#"CANCELLED");
} else {
// Use the BTDropInResult properties to update your UI
// result.paymentOptionType
// result.paymentMethod
// result.paymentIcon
// result.paymentDescription
}
}];
[self presentViewController:dropIn animated:YES completion:nil];}
i think when the customer submit the result, payment nounce will be result object?? if it thinking is right how can get that nounce to a variable to send it to my server. i am fairly new in ios, so any help will be appreciated.
For the new iOS v4 SDK I had to import Braintree also, I didn't have access to the paymentMethod property of the BTDropInResult otherwise.
import BraintreeDropIn
import Braintree
You implement BTDropInViewControllerDelegate to obtain the payment method nonce on success, and dismiss the Drop In UI in either case:
- (void)dropInViewController:(__unused BTDropInViewController *)viewController didSucceedWithPaymentMethod:(BTPaymentMethod *)paymentMethod {
[self postNonceToServer:paymentMethod.nonce]; // Send payment method nonce to your server
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)dropInViewControllerDidCancel:(__unused BTDropInViewController *)viewController {
[self dismissViewControllerAnimated:YES completion:nil];
}
Send payment method nonce to server
- (void)postNonceToServer:(NSString *)paymentMethodNonce {
// Update URL with your server
NSURL *paymentURL = [NSURL URLWithString:#"https://your-server.example.com/checkout"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:paymentURL];
request.HTTPBody = [[NSString stringWithFormat:#"payment_method_nonce=%#", paymentMethodNonce] dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// TODO: Handle success and failure
}] resume];
}
BTDropInRequest *request = [[BTDropInRequest alloc] init];
request.amount = #"10";
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientTokenOrTokenizationKey request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else if (result.cancelled) {
NSLog(#"CANCELLED");
} else {
BTPaymentMethodNonce *selectedNonce = result.paymentMethod;
[self postNonceToServer:self.selectedNonce.nonce];
}
}];
[self presentViewController:dropIn animated:YES completion:nil];
I know this question has been asked multiple time but none of them solved my problem.
Error Domain=com.quickblox.chat Code=401 "Password not verified"
actually I have tried this:
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"bdsfbd %#",chatuserobj.fullName);
NSLog(#"Chat Id %lu",(unsigned long)chatuserobj.ID);
NSLog(#"Current User %#",[QBSession currentSession].currentUser);
QBUUser *currentUserr = [QBUUser user];
currentUserr.ID = appDelegate.loginUserId;
currentUserr.password = appDelegate.loginUserPassword;
// connect to Chat
[QBRequest logInWithUserLogin:appDelegate.loginUser password:appDelegate.loginUserPassword successBlock:^(QBResponse *response, QBUUser *user)
{
chatDialog = [[QBChatDialog alloc] initWithDialogID:NULL type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(chatuserobj.ID)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog)
{
NSLog(#"Created Dialog %#",createdDialog);
} errorBlock:^(QBResponse *response)
{
NSLog(#"Error %#",response);
}];
} errorBlock:^(QBResponse *response) {
}];
[[QBChat instance] connectWithUser:chatuserobj completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
[self startChat];
}];
[QBSettings setKeepAliveInterval:30];
[QBSettings setAutoReconnectEnabled:YES];
}
and this
-(void)startChat
{
[[QBChat instance] addDelegate:self];
QBChatMessage *message = [QBChatMessage message];
[message setText:#"Hey there"];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[#"save_to_history"] = #YES;
[message setCustomParameters:params];
[chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error)
{
NSLog(#"Completed: %#",error.description);
}];
}
I don't know where I am wrong. So point out my mistake.
EDIT: again in did load
- (void)viewDidLoad
{
[super viewDidLoad];
// connect to Chat
[[QBChat instance] connectWithUser:currentUserr completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
}];
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self chat];
});
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self startChat];
});
}
chat is method for creating dialog for private group or one to one connection
-(void)chat
{
chatDialog = [[QBChatDialog alloc] initWithDialogID:NULL type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(chatuserobj.ID)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
} errorBlock:^(QBResponse *response) {
}];
}
and start chat actual communication occur
-(void)startChat
{
[[QBChat instance] addDelegate:self];
QBChatMessage *message = [QBChatMessage message];
[message setText:#"Hey there"];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[#"save_to_history"] = #YES;
[message setCustomParameters:params];
[chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error)
{
NSLog(#"Completed: %#",error.description);
}];
}
now this error occur
UserInfo={NSLocalizedRecoverySuggestion = You are not connected to chat.
- (void)viewDidLoad
{
[super viewDidLoad];
// connect to Chat
[[QBChat instance] connectWithUser:currentUserr completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self chat];
});
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self startChat];
});
}];
}
and same method of start chat and chat. it works well
[[QBChat instance] connectWithUser:chatuserobj completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
}];
This method is used to connect yourself to the chat and not your opponents. Furthermore, in order to connect with this method your QBUUser instance must have valid password set as password property.
Basically you need to connect yourself to the chat and then just start creating dialogs and sending messages.
I wrote a code to get heart rate values from Health kit. The code is working fine but when the new heart values are updated in Health kit. I have to come to main screen and then multitask my app to get the updated results. What my aim is to get the updated result on my app without reopening or multitasking it, please help as I am new to iOS development.
My code:-
-(void)get_heartRates
{
//code to get the updated heart beats
HKSampleType *sampleType =
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:sampleType
predicate:nil
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
if (error) {
NSLog(#"error occured while setting observer. %# ",
error.localizedDescription);
abort();
}
// Take whatever steps are necessary to update your app's data and UI
// This may involve executing other queries
[self executeAnchoredQuery];
// If you have subscribed for background updates you must call the completion handler here.
// completionHandler();
}];
[self.healthStore executeQuery:query];
}
-(void)executeAnchoredQuery
{
NSDate *startDate1 = [NSDate distantPast];
NSPredicate *Predicate = [HKQuery predicateForSamplesWithStartDate:startDate1 endDate:[NSDate date] options:HKQueryOptionStrictEndDate];
HKSampleType *object = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
sum_Of_HeartRates=0.0;
HKAnchoredObjectQuery *heartQuery = [[HKAnchoredObjectQuery alloc] initWithType:object predicate:Predicate anchor:self.lastAnchor limit:0 resultsHandler:^(HKAnchoredObjectQuery *query, NSArray<HKSample *> *sampleObjects, NSArray<HKDeletedObject *> *deletedObjects, HKQueryAnchor *newAnchor, NSError *error) {
NSLog(#"Sample counts:%ld",sampleObjects.count);
for(int i=0;i<(int)sampleObjects.count;i++)
{
HKQuantitySample *sample = (HKQuantitySample *)[sampleObjects objectAtIndex:i];
HKQuantity *quantity = sample.quantity;
double bpm_Values= [quantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
sum_Of_HeartRates=sum_Of_HeartRates+bpm_Values;
}
avg_heartBeats=sum_Of_HeartRates/(int)sampleObjects.count;
}];
[heartQuery setUpdateHandler:^(HKAnchoredObjectQuery *query, NSArray<HKSample *> *SampleArray, NSArray<HKDeletedObject *> *deletedObjects, HKQueryAnchor *Anchor, NSError *error) {
HKQuantitySample *sample = (HKQuantitySample *)[SampleArray objectAtIndex:0];
HKQuantity *quantity = sample.quantity;
new_Updated_Data =[quantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
NSLog(#"new quantity:%f",new_Updated_Data);
}];
[self.healthStore executeQuery:heartQuery];
NSLog(#"updated data %f",new_Updated_Data);
NSLog(#"%f", avg_heartBeats);
}
See if the following code could help you...
HKSampleType *sampleType =
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:sampleType
predicate:nil
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
if (error) {
NSLog(#"error occured while setting observer. %# ",
error.localizedDescription);
abort();
}
// Take whatever steps are necessary to update your app's data and UI
// This may involve executing other queries
[self executeAnchoredQuery];
// If you have subscribed for background updates you must call the completion handler here.
// completionHandler();
}];
[self.healthStore executeQuery:query];
and then the function where you write you anchoredQuery code, this may give you an idea of the code flow :
-(void)executeAnchoredQuery
{
HKSampleType *sampleType =
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKAnchoredObjectQuery *query =
[[HKAnchoredObjectQuery alloc]
initWithType:sampleType
predicate:nil
anchor:self.anchor
limit:HKObjectQueryNoLimit
resultsHandler:^(HKAnchoredObjectQuery * _Nonnull query,
NSArray<__kindof HKSample *> * _Nullable sampleObjects,
NSArray<HKDeletedObject *> * _Nullable deletedObjects,
HKQueryAnchor * _Nullable newAnchor,
NSError * _Nullable error) {
if (error != nil) {
// Add proper error handling here...
NSLog(#"*** Unable to query for step counts: %# ***",
error.localizedDescription);
abort();
}
// Process the results...
self.anchor = newAnchor;
for (HKQuantitySample *sample in sampleObjects) {
[self addStepCountSample:sample];
}
for (HKDeletedObject *sample in deletedObjects) {
[self removeStepCountSample:sample];
}
NSLog(#"Done!");
}];
[self.healthStore executeQuery:query];
}
Please go through apple developer docs for further details.