Im trying to update a field in a PFInstallation object. My iOS Code is:
PFInstallation *installation = [PFInstallation currentInstallation];
installation[#"userId"] = #"none";
[installation saveEventually:^(BOOL suceeded, NSError *error){
if (suceeded) {
[PFUser logOutInBackgroundWithBlock:^(NSError *error){
if (!error) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
}
else{
NSLog(#"Error Logging Out: %#", error);
}
}];
It use to work fine before, but for some reason using Parse Server I get the following error:
[Error]: at least one ID field (deviceToken, installationId) must be specified in this operation (Code: 135, Version: 1.12.0)
Anyone know how to fix this?
Related
I'm trying to develop and Action Extension for iOS9.1 that is supposed to query Parse for some data.
I've added, enabled and tested Parse in the extension and I'm successful at creating test objects and checking for current user.
I can not get the code inside Parse's query method
[query findObjectsInBackgroundWithBlock:^ to execute. LLDB just keeps skipping it so I'm really at a loss.
This code executes perfectly within the container app so I'm a bit confused.
- (void)viewDidLoad
{
[super viewDidLoad];
[Parse enableLocalDatastore];
[Parse enableDataSharingWithApplicationGroupIdentifier:#"group.com.app.slinky"
containingApplication:#"com.app.Slinky"];
[Parse setApplicationId:#"xxxxx"
clientKey:#"xxxxx"];
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *jsDict, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey];
NSString *pageTitle = jsPreprocessingResults[#"title"];
NSString *pageURL = jsPreprocessingResults[#"URL"];
if ([pageURL length] > 0) {
self.siteURL.text = pageURL;
self.URLstring = pageURL;
}
if ([pageTitle length] > 0) {
self.siteTitle.text = pageTitle;
}
});
}];
break;
}
}
}
[self queryParse];
}
-(void)queryParse{
PFQuery *query = [self.friendsRelation query];
[query orderByAscending:#"username"];
**[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
} else {
self.friends = objects;
[self.tableView reloadData];
}
}];**
}
There are some limit to fetch Objects from Parse. Read Here
One has a limit of obtaining 100 objects, but anything from 1 to 1000 has a valid limit.
You have to set the limit again for the query for next objects and skip the properties of PFQuery.
You will have to query again and again until you reach the total count.
For Example - If you have 3000 objects to be fetched, then you have to query 3 times with a count of say 1000.
OR
Call [self queryParse]; inside dispatch_async(dispatch_get_main_queue(), ^{ }); block.
Hope this might workout.
This was just a result of a 12 hour refractoring marathon :-(
When I moved the queryParse call into its own method. I lost the definition of
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
essentially sending a bad query... I don't know why it isn't return an error but regardless I can check that off now.
Thank you all for you help!
I have followed the Quick Start Guide for parse:
https://parse.com/apps/quickstart#parse_data/mobile/ios/native/existing
I'm using this code to save a PFObject:
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackground];
Below is what it looks like when my PFObject is saved to my Parse backend:
I would like to receive some sorta notification when this happens. Like an email, a text or a push notification. Anything that works.
Parse's recommended way of doing it is using Cloud Code. Once, you configure it, in the main.js file add the following function:
Parse.Cloud.afterSave("Posts", function(request) {
// Gets called when a new object is added to the 'Posts' class.
// From here you can proceed to send push notifications
});
The afterSave method it's triggered after a new object is added to the specified class. If you want to manage, whether or not an object should be saved, look into beforeSave.
Quick example I just put together: example:
-(void)saveLesson {
lesson = [PFObject objectWithClassName:#"lessons"];
PFObject *photo = [PFObject objectWithClassName:#"photoslessons"];
[photo setObject:[PFUser currentUser] forKey:#"userId"];
[photo setObject:self.photoFile forKey:#"lessonimage"];
[photo setObject:self.smallPhotoFile forKey:#"smalllessonimage"];
[photo setObject:self.thumbnailFile forKey:#"thumblessonimage"];
[photo setObject:self.originalFile forKey:#"originallessonimage"];
PFACL *publicACL = [PFACL ACL];
[publicACL setPublicWriteAccess:YES];
[publicACL setPublicReadAccess:YES];
photo.ACL = publicACL;
self.photoPostBackgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.photoPostBackgroundTaskId];
}];
[photo saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Photo uploaded");
NSNumber *userCount = [[NSNumber alloc] initWithInt:0];
lesson[#"textBody"] = [[[self contentView] descriptionText] text];
lesson[#"title"] = [[[self contentView] title] text];
lesson[#"likes"] = userCount;
lesson[#"times_viewed"] = userCount;
lesson[#"featured"] = #"false";
lesson[#"userId"] = [PFObject objectWithoutDataWithClassName:#"_User" objectId:[PFUser currentUser].objectId];
lesson[#"lessonimage"]= photo;
[lesson saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
PFQuery *query = [PFQuery queryWithClassName:#"activesessions"];
[query whereKey:#"teacherid" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (object!=nil) {
[object setObject:lesson forKey:#"lessonid"];
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[(NSHLiveStudentProfileViewController*)self.navigationController.viewControllers[0] thisIsAHack];
[[self navigationController] popToRootViewControllerAnimated:true];
}
}];
}}];
} else if (error) {
NSString * alertTitle = #"Error Saving Lesson";
UIAlertController * alert = [UIAlertController alertControllerWithTitle:alertTitle message:#"Please make sure to include both a title and description." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { }];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:^{}];
}
}];
} else {
NSLog(#"Photo failed to save: %#", error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Couldn't post your photo" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Dismiss", nil];
[alert show];
}
[[UIApplication sharedApplication] endBackgroundTask:self.photoPostBackgroundTaskId];
}];
}
So, that's a lot of code, but here's what we are going to do to help you out, lets add a method to send yourself an email:
First off, download load the pod mailgun:
then do this:
Mailgun *mailgun = [Mailgun clientWithDomain:kMailGunAPIClientDomain apiKey:kMailGunAPIKey];
Also, sign up for a mail gun account, and send an email to yourself
Then in the code above, whenever and object is saved with "success" in the block like this:
first off, obviously change this:
#"Peter Pan <peterpan.notarealemailaddress#gmail.com>"
to your mail address and then fill in the portions that require your API key and such that you get from MailGun, mailgun is cheap and this works for you immediately as soon as you set up the mail gun account (I dont work for mailgun, I'm jut a happy user)
[lesson saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[mailgun sendMessageTo:#"Peter Pan <peterpan.notarealemailaddress#gmail.com>"
from:#"Excited User <apeterpan.notarealemailaddress#gmail.com>>"
subject:#"Mailgun is awesome!"
body:#"and life is great!"];
boom!,
Here's the MailGun pod: https://github.com/rackerlabs/objc-mailgun
Sign up for a free account at: http://www.mailgun.com/
Test it out, the neat thing about this, is it's auto trigger and you can put whatever you want inside the email call that I've placed here, you also need to get AFNetworking:
get it here: https://github.com/AFNetworking/AFNetworking
This method doesn't require any sort of javascript cloudcoding and you can even substitute the body of your email for the informaiton you want to send to yourself. If you want to send yourself the USER ID of the person who just triggered your method, then there you have it, the email will be sent.
So you have this:
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackground];
Change it to this:
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"successfully SAVED!");
}
}];
this is your custom save function:
-(void)customSaveFunction {
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"successfully SAVED!");
Mailgun *mailgun = [Mailgun clientWithDomain:kMailGunAPIClientDomain apiKey:kMailGunAPIKey];
[mailgun sendMessageTo:#"Peter Pan <peterpan.notarealemailaddress#gmail.com>"
from:#"Excited User <apeterpan.notarealemailaddress#gmail.com>>"
subject:#"Mailgun is awesome!"
body:#"and life is great!"];
}
}];
}
I showed the save lesson example as something where you can send multiple emails with multiple information in the email for differnet save triggers, but this code is useless to me now, this was used for major testing purposes only, so the custom save function that I posted above these few sentences is how you would do it yourself, or just put that same code in your viewcontroller or whereever you save your objects.
I'm using Parse 1.7.1 for iOS and I am trying to sign up a user. I chose to let the user interact with the app before I force the sign up / login process. The problem is that as soon as I call signUp the session object disappears from the server and causes any subsequent calls to return error 209.
Here's some code:
AppDelegate:
[PFUser enableAutomaticUser];
[[PFUser currentUser] incrementKey:#"runCount"];
[[PFUser currentUser] saveInBackground];
When I check the Parse Core console I can see a valid User & Session.
MyController:
PFUser *user = [PFUser currentUser];
if (! user) {
user = [PFUser user];
}
user.email = #"my#email.com";
user.password = #"somepassword";
user.username = #"whatever";
// I also tried Save instead of signUpInBackground... same result
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Success signing up");
}
else {
NSLog(#"Error %#", [error localizedDescription]);
}
}];
When I check the console there is an updated user but there is no Session anymore.
Any other API call
[Error]: invalid session token (Code: 209, Version: 1.7.1)
Despite the fact that the callback returns success as soon as I try to interact with the Parse API I get a 209, invalid session token. This is fair enough, since the session object is not present in the console.
Does anyone know what I am doing wrong?
Note: this a brand new app (not affected by the change made by Parse on the 25th of March)/.
I have raised this as a bug with Facebook and they are fixing it in 1.7.3
Here's the conversation I had with them.
Create button with selector -signUpSelector. And need implement this code:
-(void)signUpSelector
[PFUser becomeInBackground:#"session-token-here" block:^(PFUser *user, NSError *error) {
PFUser *newUser = nil;
if (error) {
newUser = [PFUser user];
} else {
newUser = user;
}
user.email = #"my#email.com";
user.password = #"somepassword";
user.username = #"whatever";
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Success signing up");
}
else {
NSLog(#"Error %#", [error localizedDescription]);
}
}];
}];
I am trying to return a bool value from a class method I call that has a block in it. I get the error, Incompatible block pointer types sending.... How would I get around this? I just want to know if the class method I call completes with or without error...
+ (BOOL)saveSelectedDepartmentsToParse:(NSMutableDictionary *)dictionary {
NSArray *array = [dictionary allKeysForObject:#"YES"];
NSMutableArray *trimmedArray = [[NSMutableArray alloc] init];
for (NSString *string in array) {
NSString *final = [string removeAllInvalidCharacters];
[trimmedArray addObject:final];
}
NSLog(#"Array = %#", trimmedArray);
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation removeObjectForKey:#"channels"];
[currentInstallation addObjectsFromArray:trimmedArray forKey:#"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
[self saveDepartmentsDictionary:dictionary];
}
else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
}
}];
}
I just want to know if the class method I call
This is a misunderstanding of how asynchronous code works. When you are supplying a block to saveInBackgroundWithBlock:, that code is not executed straight away. It's executed at some later point by the Parse framework, and whichever part of Parse that does so would get the return value if the block were defined to have one, which it isn't. Your block isn't executed at the point at which you write it, so you can't return anything at the point at which you write it.
Your code isn't calling the block, and you can't return values to your code from it. It doesn't make sense to do so. If another part of your code wants to know when the saving has finished, you'll need to use some other mechanism than return values, such as calling a method from your block, posting a notification, or Key-Value Observing.
From the block keyword InBackground:
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
[self saveDepartmentsDictionary:dictionary];
} else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
}
}];
I guess the block is called asynchronously.
If you want to get the result, you can wait here until the block is executed, but this make the saveInBackgroundWithBlock useless.
So NSNotification may be better:
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
[self saveDepartmentsDictionary:dictionary];
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationParseSaveSucceeded object:nil];
} else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationParseSaveFailed object:nil];
}
}];
I have an array of string's that I am trying to save to parse as the channels for push notifications. The array is correct, put I have no idea what is going on. Can anybody throw some light on this? Thanks.
Error:
Error: Bad channel name: TestString123 (Code: 112, Version: 1.1.30)
Code:
- (void)saveSelectedDepartmentsToParse:(NSMutableDictionary *)dictionary {
NSArray *array = [dictionary allKeysForObject:#"YES"];
NSLog(#"Array = %#", array);
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addObjectsFromArray:array forKey:#"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
}
else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
}
}];
}
The reason is because Parse does not accept whitespaces or any special characters in the channels. After removing all white spaces and special characters, the operation succeeded.