I have two PFObject, I'll call them A and B for simplicity.
I create A and save it.
I create B, set a property on it called "a" pointing to A and save B
I then create a property on A called "b" and then save A
When I try to do that something does not seem to work right, I am trying to understand if it's something else in my code or if Parse does not allow me to have two PFObjects that point at each other.
Can two PFObjects point at each other?
They can indeed. I am assuming that you're not waiting for the save to complete, and it fails because of that.
If you create A and save it, create B pointing to A and save B, then you can point to B on A and save that too. The saves must be complete though, in between operations.
Regardless, it's not a recommended approach.
PFObject *post = [PFObject objectWithClassName:#"Post"];
PFObject *comment = [PFObject objectWithClassName:#"Comment"];
[post saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
[comment setObject:post forKey:#"post"];
[comment saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
[post setObject:comment forKey:#"comment"];
[post saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
// Both objects should be linked now.
}];
}];
}];
They can't (as far as I know).
I came across the same issue. My workaround was to have B store the objectId of A, and A store a pointer to B.
Related
I have an application that saves public urls and then uploads them syns a reference to them in parse when the a button is clicked. After I make the calls to parse, in the block, I want to reset the array that I'm using, but I'm a little unclear if removing the reference will create some sort of null pointer error. Here is my code
for (NSString *theString in sharedDataController.filesToUpload) {
// Create PFObject with recipe information
PFObject *parseImage = [PFObject objectWithClassName:#"Photos"];
[parseImage setObject:theString forKey:#"myURL"];
[parseImage setObject:object forKey:#"photoUser"];
[parseImage saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// Show success message
NSLog(#"Parse Image Saved");
} else {
NSLog(#"Parse Image Error");
}
}];
// if I delete the contents sharedDAtaController.filesToUpload here, will that create an issue
}
You are creating objects from array and uploading all objects one by one. But Parse also provide you a bulk upload in array. So create array of PFObjects and upload it to Parse
NSMutableArray *arrParseObjects = [[NSMutableArray alloc]init];
for (NSString *theString in sharedDataController.filesToUpload) {
// Create PFObject with recipe information
PFObject *parseImage = [PFObject objectWithClassName:#"Photos"];
[parseImage setObject:theString forKey:#"myURL"];
[parseImage setObject:object forKey:#"photoUser"];
[arrParseObjects addObject:parseImage];
// if I delete the contents sharedDAtaController.filesToUpload here, will that create an issue
}
[PFObject saveAllInBackground:arrParseObjects block:^(BOOL succeeded, NSError *PF_NULLABLE_S error){
if (succeeded) {
// You just saved all objects in Parse
}else{
}
}];
End if we talk about your crash on removing object. We never can remove any object from the array while we are looping through it. 1 solution to it is that we need to use reverse enumeration to it.
[sharedDAtaController.filesToUpload enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// Remove the object after work
// Make sure your array is a NSMutableArray.
}];
Yes it will create a problem. The calls to saveInBackgroundWithBlock are not finished outside of the completion block. You will need some method to coalesce all the calls and only delete sharedDAtaController.filesToUpload once the last completion block fires.
You could make an atomic property "pendingSaves", increment it each time before you call saveInBackgroundWithBlock, decrement it in the completion block, and delete sharedDAtaController.filesToUpload in the completion block of saveInBackgroundWithBlock only if pendingSaves == 0
Im using Parse for server side. And I have a table view with list of Contacts object from Parse. If user taps on object it saves it to parse and if taps again it deletes it from parse.
For saving I use method:
- (void)addContact:(Contact *)contact withBlock:(void (^)(void))completion {
[contact saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (completion) completion();
}];
}
For deleting use this:
- (void)removeContact:(Contact *)contact withBlock:(void (^)(void))completion {
[contact deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
contact.objectId = nil;
if (completion) completion();
}];
}
I set the objectId to nil because I use this property in table view to see if object is allready on parse of it is just on the phone.
The problem is that if user do steps like: save, delete, save.
Save: the object is created on parse with all the data.
Delete: the object is deleted from parse.
Save: the object is created on parse but without data (just objectId).
Is this the normal procedure? On the phone the object has allways all the data even after deletetion method. So I assume if I run save method on the object with all the data, that it will save it to the parse, even if the same object goes through deletion in the past.
Here's a picture of one empty object and one that is saved correctly with all the data:
What are your experience with this? Enjoy resolving this issue and help making wold a better place :)
Setting the object id to nil as you are is relying on private and undocumented features of the PFObject class. Even if it did work now it isn't guaranteed to always work.
You should either not delete the object and simply set a flag to show that it has been removed / deleted and use that for your logic.
Or, you should actually discard the local object once it's deleted and create a new object with a copy of the old objects values.
My app is a messaging style app and in it you can "tag" another user. (A bit like twitter).
Now, when this message is displayed, the avatar belonging to the person(s) who was tagged is displayed with that message.
The avatar of the user is stored as a PFFile against the PFUser object.
I'm loading it something like this...
PFImageView *parseImageView = ...
[taggedUser fetchIfNeededInBackgroundWithBlock:^(PFObject *user, NSError *error) {
parseImageView.file = user[#"avatar"];
[parseImageView loadInBackground];
}];
This all works fine.
The load if needed part of the code will most of the time not touch the network as for the majority of the time it has the user data cached.
However, the load in background part that gets the image and puts it into the image view runs every single time. There doesn't seem to be any caching on the PFFile data at all.
Even after downloading the same user's avatar numerous times it still goes to the network to get it.
Is there a way to get this data to cache or is this something I'll have to implement myself?
PFFile will automatically cache the file for you, if the previous PFQuery uses caching policy such as:
PFQuery *query = [PFQuery queryWithClassName:#"MyClass"];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
To check whether the PFFile is in local cache, use:
#property (assign, readonly) BOOL isDataAvailable
For example:
PFFile *file = [self.array objectForKey:#"File"];
if ([file isDataAvailable])
{
// no need to do query, it's already there
// you can use the cached file
} else
{
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error)
{
if (!error)
{
// use the newly retrieved data
}
}];
}
Hope it helps :)
In the end I created a singleton with an NSCache and queried this before going to Parse.
Works as a quick stop for now. Of course, it means that each new session has to download all the images again but it's a lot better now than it was.
You can cache result of PFQuery like below code..And need to check for cache without finding objects in background everytime..while retrieving the image.It has some other cache policies also..Please check attached link also..
PFQuery *attributesQuery = [PFQuery queryWithClassName:#"YourClassName"];
attributesQuery.cachePolicy = kPFCachePolicyCacheElseNetwork; //load cache if not then load network
if ([attributesQuery hasCachedResult]){
NSLog(#"hasCached result");
}else{
NSLog(#"noCached result");
}
Source:https://parse.com/questions/hascachedresult-always-returns-no
Hope it helps you....!
I want to save PFObjects from a Parse query in an NSMutableArray that my class has called listdata. I will later use the listdata array. When I traced through my code, it updated the highScoreObjects array for each object found. But when I try to set the listdata array to the highScoreObjects array, the highScoreObjects array is empty. Is there a way to keep the data after the query ends?
NSMutableArray *highScoreObjects = [[NSMutableArray alloc] initWithCapacity:5];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
[highScoreObjects addObject:object];
NSLog(#"%#", object.objectId);
}
dispatch_async(dispatch_get_main_queue(), ^ {
[self.tableView reloadData];
});
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
self.listData = highScoreObjects;
I also tried keeping the line self.listData = highScoreObjects;
inside the self.listData = highScoreObjects; loop. This didn't make any difference.
It isn't that it isn't set. It's that it isn't set yet. This is because you're using findObjectsInBackgroundWithBlock and the asynchronous process hasn't completed yet.
Move your assignment (self.listData = highScoreObjects;) into the block, just before you dispatch the request to reload the table view.
This is yet another case of not understanding the nature of asynchronous programming.
Consider this situation:
You want to make an egg sandwich. You put the eggs on to boil, and set an alarm for when they're cooked to get them out, peel them, cut them up and add them to your sandwich. While you wait you get the bread and butter it, then wait for the alarm to go off.
Your call to findObjectsInBackgroundWithBlock is putting the eggs on to boil. The block you pass it is the alarm and what you plan to do with the eggs once cooked.
Your code above is akin to putting the eggs on to boil, then straight away trying to use the uncooked/partially-cooked eggs on your sandwich. Makes a big mess.
The solution is to call a method at the end of the block your pass to the method.
I have looked and looked in Parse docs, SO and Google, and can not find an example of storing a plain ol' Core Data SQLite file to Parse.com. Initially I just want to store the Core Data file as a backup; eventually I want to add FTASync and then ability for others to utilize the stored Core Data file from this iOS app.
Is there an example of doing this without using a PFObject? Can someone point me to a place in the Parse docs where I can find out how to do this?
No, you cannot do this without any PFObject. Theoretically you can save backups just with
- (void)createBackupFromSQLiteStorageAtPath:(NSString*)path
{
NSString *name = [[NSDate date] description]; // for example, stringified date will act as name
PFFile *backup = [PFFile fileWithName:name contentsAtPath:path];
[backup saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (error)
{
// handle
}
else
{
// success
}
}];
}
But! If you want to access it from parse's fileserver you'll need to keep PFFile objects somehow (you can also store PFFile's url property - but it's hack) - and here's the case where PFObject comes to help. Assuming you have backed up your store already:
- (void)storeBackupFile:(PFFile*)file
{
PFObject *backup = [PFObject objectWithClassName:#"Backup"];
[backup setObject:file forKey:#"file"];
[backup setObject:[PFUser currentUser] forKey:#"user"];
[backup saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (error)
{
[backup saveEventually];
}
else
{
// success
}
}];
}
So after this you'll have Backup object in parse database, with link to backup file and user that created backup.
Some more considerations:
1) It's good to organize such backup as NSOperation subclass.
2) It's bad idea to store backups with Parse in such way. File storage on Parse is very expensive resource. Also, PFFile has local cache - your storage will be duplicated each time you make backup, so app's size will increase dramatically with often backups.