Service unavailable error (CKError Code 6) in CKContainer requestApplicationPermission - ios

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);
}];
}
}];
}
}];
}
}];
}
}];

Related

NSInvalidArgumentException Invalid type in Json write objective c

I am new to objective c. I get NSInvalidArgumentException Invalid type in Json write (NSError) at the following line.
NSURLSessionDataTask *result = [self POST:#"/analytics"
parameters:params completion:^(OVCResponse * _Nullable response, NSError * _Nullable error) {
[self handleResponse:response error:error adjustErrorBlock:self.commonErrorAdjustBlock completion:theCompletion];
}];
The code is the following
NSArray *params = [MTLJSONAdapter JSONArrayFromModels:events error:NULL];
void(^theCompletion)(id _Nullable, NSError * _Nullable) = ^(id _Nullable result, NSError * _Nullable error) {
completion(error);
};
NSURLSessionDataTask *result = [self POST:#"/analytics/"
parameters:params completion:^(OVCResponse * _Nullable response, NSError * _Nullable error) {
[self handleResponse:response error:error adjustErrorBlock:self.commonErrorAdjustBlock completion:theCompletion];
}];
handleresponse method
- (void)handleResponse:(nullable OVCResponse *)response error:(nullable NSError *)error adjustErrorBlock:(nullable C8ApiClientv2AdjustErrorBlock)adjustErrorBlock completion:(void (^)(id _Nullable result, NSError * _Nullable error))completion {
DebugLog(#"\n===[start response] <%#> \n===%#, \nerror:%#\n===[end response] <%#> ", self.class, response, error, self.class);
NSError *adjustedError = nil;
if (NULL != adjustErrorBlock){
adjustedError = adjustErrorBlock(error, response);
}
if (nil != adjustedError) {
completion(nil, adjustedError);
} else {
if (response.HTTPResponse.statusCode >= 200 && response.HTTPResponse.statusCode <= 299) {
NSAssert(error == nil, #"got error though request succeded");
}
else if (response.HTTPResponse.statusCode == 401) {
error = NSError.com_eight_APIServiceUnauthorizedError;
}
else if (response.HTTPResponse.statusCode >= 500 && response.HTTPResponse.statusCode <= 599) {
error = nil != error ? error : NSError.com_eight_APIServiceUnknownError;
[SentrySDK captureMessage:[NSString stringWithFormat:#"Request failed: internal server error (500):\n%#", response]];
}
else {
error = nil != error ? error : NSError.com_eight_APIServiceUnknownError;
}
id result = nil == error ? response.result : nil;
completion(result, error);
}
}
Trace
-[AFJSONRequestSerializer requestBySerializingRequest:withParameters:error:]
(AFNetworking/AFNetworking/AFURLRequestSerialization.m:1312)
8
+0x01f480
-[AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:]
(AFNetworking/AFNetworking/AFURLRequestSerialization.m:407)
8
+0x161de8
-[OVCHTTPSessionManager _dataTaskWithHTTPMethod:URLString:parameters:completion:]
(Overcoat/sources/Core/OVCHTTPSessionManager.m:74)
8
+0x1621f0
-[OVCHTTPSessionManager POST:parameters:completion:]
(Overcoat/sources/Core/OVCHTTPSessionManager.m:129)
8
+0x42d98c
-[C8AnalyticsClientv2 trackEvents:completion:]
(DAL/Networkingv2/Clients/C8AnalyticsClientv2.m:40)
Any help appreciated.

Unable to share a CKRecord

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.

What is the recommended Process for using QuickBlox in iOS Share Extension

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) {
}];

Parse fetching objects from a few classes returns 0 (iOS)

I'm trying to fetch objects from a few classes at the same time, but all of them except 1 returning 0 objects.
I really don't know what to do.
My code:
PFQuery *query=[PFQuery queryWithClassName:#"Dogs"];
query.limit=1000;
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.dogs=[[NSArray alloc] initWithArray:objects];
}];
query=[PFQuery queryWithClassName:#"Cats"];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.cats=[[NSArray alloc] initWithArray:objects];
}];
query=[PFQuery queryWithClassName:#"Mice"];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.mice=[[NSArray alloc] initWithArray:objects];
}];
query=[PFQuery queryWithClassName:#"Elephants"];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.elephants=[[NSArray alloc] initWithArray:objects];
}];
query=[PFQuery queryWithClassName:#"Lions"];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.lions=[[NSArray alloc] initWithArray:objects];
}];
Does anybody have an idea why is it?
Thank you!
you should use different references for PFQuery - -
PFQuery *query1=[PFQuery queryWithClassName:#"Dogs"];
query1.limit=1000;
[query1 findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.dogs=[[NSArray alloc] initWithArray:objects];
}];
PFQuery query2=[PFQuery queryWithClassName:#"Cats"];
[query2 findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.cats=[[NSArray alloc] initWithArray:objects];
}];
PFQuery query3=[PFQuery queryWithClassName:#"Mice"];
[query3 findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.mice=[[NSArray alloc] initWithArray:objects];
}];
PFQuery query4=[PFQuery queryWithClassName:#"Elephants"];
[query4 findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.elephants=[[NSArray alloc] initWithArray:objects];
}];
PFQuery query5=[PFQuery queryWithClassName:#"Lions"];
[query5 findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#",[error description]);
}
self.lions=[[NSArray alloc] initWithArray:objects];
}];

How to get user profile image from logged user with Twitter Kit/Fabric in iOS

i'm using Fabric in order to add a "Sign In with Twitter" button in my app. Sign in process works fine, but I don't how (if it's possible) how to get the logged user's profile image.
Is there any property inside TWTRSession with this info (like userName and userID)? Looked for it but I didn't found anything?
[TWTRLogInButton buttonWithLogInCompletion:^(TWTRSession *session, NSError *error) {
// I get user data here...
// and need to get user Twitter's profile image
}];
Thanks
EDIT
Thanks to sak's answer, i figured out how to do it.
[TWTRLogInButton buttonWithLogInCompletion:^(TWTRSession *session, NSError *error) {
[[[Twitter sharedInstance] APIClient] loadUserWithID:session.userID completion:^(TWTRUser *user, NSError *error) {
NSLog(#"User image %#", user.profileImageURL);
}];
}];
For those who are getting this error :
Value of type 'Twitter' has no member 'APIClient'
This helped me,
let twitterClient = TWTRAPIClient(userID: userID)
twitterClient.loadUserWithID(userID) { (user:TWTRUser?, error:NSError?) in
print(user?.profileImageURL)
}
Try below code to get profile image -
Swift 3
let twitterClient = TWTRAPIClient(userID: session?.userID)
twitterClient.loadUser(withID: (session?.userID)!, completion: { (user, error) in
print(user!.profileImageURL)
})
The way to do it in swift is as follows:
Twitter.sharedInstance().APIClient.loadUserWithID( session.userID )
{
(user, error) -> Void in
if( user != nil )
{
println( user.profileImageURL )
}
}
I hope this helps.
I don't think the TWTRSession object has the user's image, but you can use the session to request a TWTRUser object, and that will have what you need.
Try this:
TWTRLogInButton *logInButton = [TWTRLogInButton buttonWithLogInCompletion:^(TWTRSession *session, NSError *error) {
// play with Twitter session
if (session) {
NSLog(#"Twitter signed in as -> name = %# id = %# ", [session userName],[session userID]);
/* Get user info */
[[[Twitter sharedInstance] APIClient] loadUserWithID:[session userID]
completion:^(TWTRUser *user,
NSError *error)
{
// handle the response or error
if (![error isEqual:nil]) {
NSLog(#"Twitter info -> user = %# ",user);
NSString *urlString = [[NSString alloc]initWithString:user.profileImageLargeURL];
NSURL *url = [[NSURL alloc]initWithString:urlString];
NSData *pullTwitterPP = [[NSData alloc]initWithContentsOfURL:url];
UIImage *profImage = [UIImage imageWithData:pullTwitterPP];
} else {
NSLog(#"Twitter error getting profile : %#", [error localizedDescription]);
}
}];
} else {
NSLog(#"Twitter error signed in : %#", [error localizedDescription]);
}
}];
logInButton.center = self.view.center;
[self.view addSubview:logInButton];
To get the picture large size, you can build a URL as follow:
if let twitterId = session.userID{
let twitterClient = TWTRAPIClient(userID: twitterId)
twitterClient.loadUser(withID: twitterId) {(user, error) in
if let userName = user?.screenName{
let url = "https://twitter.com/\(userName)/profile_image?size=original")
}
}
}
This approach uses the twitter user name, then it will be redirected to the real profile picture size.
[[[Twitter sharedInstance] APIClient] loadUserWithID:[session userID] completion:^(TWTRUser *user, NSError *error) {
}];
Here is how I get userId,userName,authToken,authTokenSecret and profileImageURL
[[Twitter sharedInstance] logInWithCompletion:^(TWTRSession *session, NSError *error) {
if (session != nil) {
SPTWUser *user = [[SPTWUser alloc] init];
user.userID = session.userID;
user.userName = session.userName;
user.authToken = session.authToken;
user.authTokenSecret = session.authTokenSecret;
TWTRAPIClient *client = [[TWTRAPIClient alloc] initWithUserID:user.userID];
[client loadUserWithID:user.userID completion:^(TWTRUser * _Nullable user, NSError * _Nullable error) {
NSLog(#"%#",user.profileImageURL);
}];
}
}];
Reference TWTRAPIClient Class, TWTRUser Class,TWTRSession Class

Resources