Kinvey iOS query all users - ios

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.

Related

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.

iOS Parse Stripe Integration

I'm fairly new to programming and I created an app to charge customers and would like to store their CC information and charge it at a later time. I've been going through all the tutorials and documentation and I am unable to follow how I can integrate this into my app. Do I need to know other technical skills such as Rest API, Curl, Ruby, etc to get this set up? All the guides and documentation is pointing to that direction. I don't really understand what GET/POST is for and how that fits into iOS Objective-C programming.
Any guidance on how to set this up would be tremendously appreciated. I've been stuck on this for some time now.
Parse's stripe API is not as complete as it could and should be. There are many features it does not include natively, but can be accomplished VIA an HTTP Request. I had to learn a little bit of Javascript, and HTTP request to get many features working. Of course your first instinct should tell you do NOT store a CC number on any device ever! Anytime you have a user input a CC number, immediately get a token and then that is all you will need to use.
Luckily stripe gives you the ability to save customers, and attached CC to customers, and then charge that customer in the future without getting the CC number again. Parse's api does not handle adding a CC to a customer so I added the feature myself.
So Step 1 and 2 Generate a Customer using Parse's API, and generate a Token from the CC information they enter again using Parse's API. If you need help with this, and the cloud code required let me know.
Step 3 Add a CC to a customer. I'm using a custom Customer object, but the main thing you really need is the stripe customerId which is customer.identifier in my code, and tokenID from your CC which in my case is token.tokenId. The response back will be a JSON string with the card information, I turn this into a Dictionary, and then create a STPCard from the dictionary. Also I show how to remove a card from a customer.
iOS Code:
+(void)addToken:(STPToken *)token toCustomerId:(NSString *)customerId completionHandler:(PFIdResultBlock)block
{
[PFCloud callFunctionInBackground:#"stripeUpdateCustomer" withParameters:#{#"customerId":customerId,#"data":#{#"card":token.tokenId}} block:block];
}
+ (void)removeCard:(STPCard *)card FromCustomer:(ELCustomer *)customer completion:(STPCardDeletionBlock)handler
{
if (!customer ||!customer.identifier || !card || !card.identifier || !handler) [NSException raise:#"RequiredParameter" format:#"Required Parameter Missing for deleting card from customer"];
[PFCloud callFunctionInBackground:#"stripeDeleteCardFromCustomer" withParameters:#{#"cardId":card.identifier,#"customerId":customer.identifier} block:^(id object, NSError *error)
{
NSDictionary *dict = nil;
NSError *jsonError = nil;
if (object && [object isKindOfClass:[NSString class]] && !error) {
dict = [NSJSONSerialization JSONObjectWithData:[object dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&jsonError];
}
if (!jsonError && dict) {
handler(dict[#"id"],[dict[#"deleted"] boolValue],error);
}
else if(jsonError) handler(nil,NO,jsonError);
else handler(nil,NO,error);
}];
}
Cloud Code Required:
Parse.Cloud.define("stripeUpdateCustomer", function(request, response)
{
Stripe.Customers.update
(
request.params["customerId"],
request.params["data"],
{
success:function(results)
{
console.log(results["id"]);
response.success(results);
},
error:function(error)
{
response.error("Error:" +error);
}
}
);
});
Parse.Cloud.define("stripeDeleteCardFromCustomer", function(request, response)
{
Stripe.initialize(STRIPE_SECRET_KEY);
Parse.Cloud.httpRequest({
method:"DELETE",
//STRIPE_SECRET_KEY will be your stripe secrect key obviously, this is different from the public key that you will use in your iOS/Android side.
// STRIPE_API_BASE_URL = 'api.stripe.com/v1'
url: "https://" + STRIPE_SECRET_KEY + ':#' + STRIPE_API_BASE_URL + "/customers/" + request.params.customerId + "/cards/" + request.params.cardId,
success: function(httpResponse) {
response.success(httpResponse.text);
},
error: function(httpResponse) {
response.error('Request failed with response code ' + httpResponse.status);
}
});
});
iOS Code for applying a charge to a customer or token notice the required parameters in the dictionary are an amount in cents not dollars, a currency, and then either a customer or a tokenId. Note a customer can have many credit cards, but one of them is the active credit card. The active card is the card that will be charged when you charge a customer:
//Will attempt to charge customer, if no customer exists, or it fails to charge the custoemr it will attempt to charge a card token directly;
//*********Warning: This is the final step it will APPLY A CHARGE TO THE ACCOUNT.***************
-(void)processChargeThroughStripeWithCompletionHandler:(STPChargeCompletionHandler)handler
{
if (![self validForCardProcessing] && ![self validForCustomerProcessing]) {
handler(nil,[NSError errorWithDomain:MY_ERROR_DOMAIN code:elErrorCodeNoCustomerOrTokenID userInfo:[NSDictionary dictionary]]);
return;
}
[self processChargeThroughStripeUsingCustomerWithCompletionHandler:^(STPCharge *charge, NSError *error)
{
if (!error) handler(charge,error);
else{
[self processChargeThroughStripeUsingCardWithCompletionHandler:^(STPCharge *charge, NSError *error) {
handler(charge, error);
}];
}
}];
}
//Process payment using a customer to their active card. No token is required if customer exists with a card on record.
//*********Warning: This is the final step it will APPLY A CHARGE TO THE ACCOUNT.***************
-(void)processChargeThroughStripeUsingCustomerWithCompletionHandler:(STPChargeCompletionHandler)handler
{
if (!self.validForCustomerProcessing)
{
handler(self,[NSError errorWithDomain:MY_ERROR_DOMAIN code:elErrorCodeNoCustomerID userInfo:[NSDictionary dictionary]]);
return;
}
[PFCloud callFunctionInBackground:#"chargeToken" withParameters:[STPCharge dictionaryFromSTPChargeForProccessingUsingCustomer:self] block:^(id object, NSError *error)
{
if (!error)
{
[self initSelfWithDictionary:object];
NSLog(#"object:%#",object);
}
handler(self,error);
}];
}
//Process payment using a token that is attached to the charge, when complete self will be updated with the new charge information
//*********Warning: This is the final step it will APPLY A CHARGE TO THE ACCOUNT.***************
-(void)processChargeThroughStripeUsingCardWithCompletionHandler:(STPChargeCompletionHandler)handler
{
if (!self.validForCardProcessing)
{
handler(self,[NSError errorWithDomain:MY_ERROR_DOMAIN code:elErrorCodeNoTokenID userInfo:[NSDictionary dictionary]]);
return;
}
[PFCloud callFunctionInBackground:#"chargeToken" withParameters:[STPCharge dictionaryFromSTPChargeForProccessingUsingCard:self] block:^(id object, NSError *error)
{
if (!error)
{
[self initSelfWithDictionary:object];
}
handler(self,error);
}];
}
+ (NSDictionary *)dictionaryFromSTPChargeForProccessingUsingCard:(STPCharge *)charge
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[#"amount"] = charge.amountInCents;
dictionary[#"currency"] = charge.currency;
dictionary[#"card"] = charge.token.tokenId;
return dictionary;
}
+ (NSDictionary *)dictionaryFromSTPChargeForProccessingUsingCustomer:(STPCharge *)charge
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[#"amount"] = charge.amountInCents;
dictionary[#"currency"] = charge.currency;
dictionary[#"customer"] = charge.customer.identifier;
return dictionary;
}
Cloud code for charging a customer/token:
Parse.Cloud.define("chargeToken",function(request,response)
{
Stripe.initialize(STRIPE_SECRET_KEY);
Stripe.Charges.create
(
request.params,
{
success:function(results)
{
response.success(results);
},
error:function(error)
{
response.error("Error:" +error);
}
}
);
});
How are you storing their CC information to charge it at a later time? Before proceeding, you need to know if it is PCI compliant or not. At most, the only things you should be looking to store is the expiration date, last 4 digits, and an associated record object that Parse Stripe gives you that corresponds to that CC. Do not try to store the full CC.
As to your other questions:
Generally you need to know a web language to do something like this. Here is an example of a possible stack that I've seen in a situation like this:
iOS App -> sends request to Server (rails, python, php, etc) -> Will send request to 3rd party site
3rd party site response -> Server -> iOS app.
The point of the server is to intercept the call from the mobile App to Parse, and the response from Parse back to the mobile app. The reason for this is so you can have a "master" db of the transactions/states and can recover if the app is ever reinstalled on the user's phone. It also will let you store an identifier that points to the user's CC on parse stripe (I'm assuming).
You should really understand GET/POST as they are becoming a very basic feature of any iOS app. They are simply how you get/insert records from a server. Considering almost all of the popular apps have some kind of network connectivity embedded in them, it really is a core part of iOS programming IMO.

Add photo attribute to user collection in Kinvey

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

Resources