Add photo attribute to user collection in Kinvey - ios

I wanted to add a profile photo to user collection when creating a user. I was thinking of doing [user setValue:imageData forAttribute:#"photo"] and then with help of KCSLinkedAppdataStore store it. But it fails with error JSON serialization not supported for NSConcreteMutableData.
[user setValue:imageData forAttribute:#"photo"];
KCSLinkedAppdataStore* store = [KCSLinkedAppdataStore storeWithCollection:[KCSCollection userCollection] options:nil];
[store saveObject:user withCompletionBlock:^(NSArray *objectsOrNil, NSError *errorOrNil) {
if (errorOrNil == nil) {
//was successful!
} else {
//failed
}
} withProgressBlock:nil];
Uploading the file separately and then set its fileid to an attribute in user collection works. What is the best way of extending KCSUser? I am currently using class extension.

Are you trying to associate the profile photo with the user?
http://support.kinvey.com/hc/communities/public/questions/200341553-Link-users-to-file-uploads?locale=en-us
Does this help answer your question?
Caroline

Related

Downloading Files from Kinvey in iOS

I have read documents over kinvey here:
http://devcenter.kinvey.com/ios/guides/files#DownloadingtoCachesDirectory
They have provided sample function as well:
[KCSFileStore downloadData:#"myId" completionBlock:^(NSArray *downloadedResources, NSError *error) {
if (error == nil) {
KCSFile* file = downloadedResources[0];
NSData* fileData = file.data;
id outputObject = nil;
if ([file.mimeType hasPrefix:#"text"]) {
outputObject = [[NSString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];
} else if ([file.mimeType hasPrefix:#"image"]) {
outputObject = [UIImage imageWithData:fileData];
}
NSLog(#"downloaded: %#", outputObject);
} else {
NSLog(#"Got an error: %#", error);
}
} progressBlock:nil];
But when i replaced "myId" with file id it gave me this error:
Error Domain=KCSServerErrorDomain Code=401 "The credentials used to authenticate this request are not authorized to run this operation. Please retry your request with appropriate credentials"
While i can access other collections i have created over kinvey (same user) with same credentials (secret,api_key)
Is there any other requirement before calling this function?
Thanks
I had the same problem. I reached out and got help from the kinvey support team.
I solved the problem by setting the "acl" for each file that I upload. According to the iOS Files guide under upload options: "By default the file will only be available to the creating user.", this was what was causing the problem for me.
I upload files like this now:
let metadata = KCSMetadata()
metadata.setGloballyReadable(true)
let data = UIImagePNGRepresentation(image)
KCSFileStore.uploadData(
data,
options: options: [KCSFileACL : metadata],
completionBlock: {KCSFileUploadCompletionBlock!},
progressBlock: nil)
This allows anyuser to read that specific file. I Use a KCSUser.createAutogeneratedUser() to read the files afterwards.
Yes, you will have access to other collections under the same kinvey app, but the permissions needs to be set for the files collection. So the file you describe as "myId" needs to have the proper permissions set, before it can be downloaded by another user than the user that upload the file.
You must be an active user for calling this function. You just need to make sure you have successfully logged in.

Seems impossible to delete a subscription in CloudKit? `-deleteSubscriptionWithID` always returns true

I'm hoping there's an experienced CloudKit guru out there, but based off my google search queries, I'm not sure if you exist. I think this may be a bug with Apple, but I can't be sure :\
I can save a subscription to my CKDatabase fine, no problems at all.
[publicDatabase saveSubscription:subscription completionHandler:^(CKSubscription *subscription, NSError *error) {
if (error)
{
//No big deal, don't do anything.
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:[subscription subscriptionID] forKey:#"SUBSCRIPTION"];
}
}];
Whenever I change a field in my record, I get a push notification, and everything is happy.
My problem is removing this subscription.
I have tried calling -deleteSubscriptionWithID:completionHandler:
As you can see in the above code snippet, I save off the subscription ID (Have also confirmed it to be the correct subscription ID by calling -fetchAllSubscriptionsWithCompletionHandler:
I passed the subscriptionID in that message, like so:
[publicDatabase deleteSubscriptionWithID:[[NSUserDefaults standardUserDefaults] objectForKey:#"SUBSCRIPTION"] completionHandler:^(NSString * _Nullable subscriptionID, NSError * _Nullable error) {
if( error ) {
NSLog(#"ERROR: %#", [error description] );
}
else
{
NSLog(#"SUCCESS: %#", subscriptionID);
}
}];
But it doesn't delete my subscription:
And no matter what I pass as the subscriptionID, there is no error and I see "SUCCESS" upon "deleting".
...so yeah. Clearly that isn't going to work.
If I manually delete the subscription through the Cloudkit Dashboard, my -fetch call properly notices that and returns an empty array:
So at this point I'm certain that I'm either deleting a subscription incorrectly in code, or it's broken and (not likely) nobody has asked on SO or any other forum that I can find?
I have also tried using a CKModifySubscriptionsOperation
CKModifySubscriptionsOperation *deleteSub = [[CKModifySubscriptionsOperation alloc] initWithSubscriptionsToSave:nil subscriptionIDsToDelete:#[[[NSUserDefaults standardUserDefaults] objectForKey:#"SUBSCRIPTION"]]];
[publicDatabase addOperation:deleteSub];
No results :(
I delete subscriptions using the database.deleteSubscriptionWithID function.
If you want to make sure that the ID is correct you could also first fetch all of them using database.fetchAllSubscriptionsWithCompletionHandler({subscriptions, error in
Then in the completion handler check if it's a valid subscription using: if let subscription: CKSubscription = subscriptionObject
And then delete one or more using: database.deleteSubscriptionWithID(subscription.subscriptionID, completionHandler: {subscriptionId, error in
Here you can see code how I delete all subscriptions:
https://github.com/evermeer/EVCloudKitDao/blob/1bfa936cb46c5a2ca75f080d90a3c02e925b7e56/AppMessage/AppMessage/CloudKit/EVCloudKitDao.swift#L897-897

How to enable user to add friends via friend's username and display user's friend list in tableview with swift

I'm currently developing iOS App with Swift that users can add friends via username in one view and display user's friend list in another tableview,I'm currently using parse and I'm able to get my app to let user sign up and log in.
I want to know the code of adding friends via username and display user's friend list with parse,
I've tried looking for this solution and I got nothing but how to get friend list from Facebook which is not related to my app .
Any help is appreciated and Let me know if you need any additional information! ( sorry for my bad english).
You need to use the FBSDK for iOS in order to make a graph request for the Facebook friends list.
I don't quite understand if you want to add a "friend" that already is registered in your app or that has a Facebook account, but in any case you'll need to store your users in a data store, i believe Parse has as PFObject which you can save like this:
var appUser = PFObject(className:"AppUser")
appUser["userFullName"] = "John Doe"
appUser["userFacebookID"] = 1
appUser["userEmail"] = "j.doe#doe.com"
appUser.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}
From there what you need is to associate the users, with a "Friendship" object for example, like this:
var userFriendship = PFObject(className:"Friendship")
appUser["invitedUserEmail"] = "jane.doe#doe.com"
appUser["invitingUserEmail"] = "j.doe#doe.com"
appUser["invitationStatus"] = "pending"
appUser.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}
After that you can update the objet in order to change the invitation status to "accepted" or "denied, "cancel" etc.
To get a the list of user invitations and friends you need to make a ParseQuery with the arguments you're looking for, such as:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"invitingUserEmail = 'j.doe#doe.com' AND invitationStatus = 'accepted'"];
PFQuery *query = [PFQuery queryWithClassName:#"Friendship" predicate:predicate];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d friends.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
You can find a lot more in the Parse Docs here https://parse.com/docs/ios/guide

Gigya phone registration

I'm using Gigya as a single-sign-on system for my iOS app.
It is integrated and I can log in with both Twitter, Facebook and manual email registration.
As both Facebook and Twitter do not return mobile phone numbers, I'm appending this information after successful registration/login along with some other information like e-mail. I am able to successfully update fields in the profile like username, nickname etc, but not phones.
A description of the profile structure can be found here:
http://developers.gigya.com/020_Client_API/020_Accounts/010_Objects/Profile
So I figure to post:
{#"phones": #[#{#"number" : _phoneNumberTextfield.text}]}
as the profile content. Which is apparently alright, since the response has statusReason OK.
All good, and if I add other fields, they get updated. But when I retrieve the profile, there is no phone number there. I tried to append the field "type" as per the definition, but then I get: 400025 Write access validation error.
So the update call tells me everything is OK, but it isn't appending the number to the profile. Adding the type to each number entry in the array of #"phones" gives an access violation.
I've been through Gigya's API spec and can't find any working examples or even JSON examples of this situation; does anyone have a solution for this?
If you're retrieving the profile using accounts.getAccountInfo, make sure you include the "extraProfileFields = phones" parameter. The phones array will not be returned by default.
http://developers.gigya.com/037_API_reference/020_Accounts/accounts.getAccountInfo
The Gigya SDK on the server-side represents data as JSON objects, which have the ability to represent nested objects under a key or arrays.
In the case of the "profile.phone" property on the account, this is stored as an array of objects, as detailed below:
{
"profile": {
"phones": [
{ "type": "phone", "number": "8005551234" },
{ "type": "cell", "number": "8885551234" }
]
}
}
Typically, the when using Gigya's iOS API, it is common to maps these JSON concepts to the the NSMutableDictionary and NSMutableArray classes respectively and then to serialize the data using the NSJSONSerialization class.
So, for example, if we wanted to set the phone numbers on an account with Gigya like shown above, then you would need to accomplish this using something like the following code:
NSMutableDictionary *phone1 = [NSMutableDictionary dictionary];
[phone1 setObject:#"phone" forKey:#"type"];
[phone1 setObject:#"8005551234" forKey:#"number"];
NSMutableDictionary *phone2 = [NSMutableDictionary dictionary];
[phone2 setObject:#"cell" forKey:#"type"];
[phone2 setObject:#"8885551234" forKey:#"number"];
NSMutableArray *phones = [NSMutableArray array];
[phones addObject:phone1];
[phones addObject:phone2];
NSMutableDictionary *profile = [NSMutableDictionary dictionary];
[profile setObject:phones forKey:#"phones"];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:profile
options:0
error:&error];
GSRequest *request = [GSRequest requestForMethod:#"accounts.setAccountInfo"];
[request.parameters setObject:jsonString forKey:#"profile"];
[request sendWithResponseHandler:^(GSResponse *response, NSError *error) {
if (!error) {
NSLog(#"Success");
// Success! Use the response object.
}
else {
NSLog(#"error");
// Check the error code according to the GSErrorCode enum, and handle it.
}
}];
Alternatively, you could construct a JSON string directly; but the above strategy tends to be much more flexible to any changes that need to be done when adding new properties.

Kinvey iOS query all users

From Kinvey documentation this is the method to use for querying users:
To query the user collection we recommend instead using
+[KCSUserDiscovery lookupUsersForFieldsAndValues:completionBlock:progressBlock:]. This
method allows you to supply a dictionary of exact matches for special
fields.
Fields for lookup:
KCSUserAttributeUsername
KCSUserAttributeSurname
KCSUserAttributeGivenname
KCSUserAttributeEmail
KCSUserAttributeFacebookId
[KCSUserDiscovery lookupUsersForFieldsAndValues:#{ KCSUserAttributeSurname : #"Smith"}
completionBlock:^(NSArray *objectsOrNil, NSError *errorOrNil) {
if (errorOrNil == nil) {
//array of matching KCSUser objects
NSLog(#"Found %d Smiths", objectsOrNil.count);
} else {
NSLog(#"Got An error: %#", errorOrNil);
}
}
progressBlock:nil];
But if I send empty dictionary, I get an error. So what to put in dictionary to get all the users?
Thank you guys, happy holidays
To get all the users, you can use the regular app data querying API.
For example,
KCSAppdataStore* store = [KCSAppdataStore storeWithCollection:[KCSCollection userCollection] options:nil];
[store queryWithQuery:[KCSQuery query] withCompletionBlock:^(NSArray *objectsOrNil, NSError *errorOrNil) {
//handle completion
} withProgressBlock:nil];
This will get a list of all the users the active user has permission to access.

Resources