I am attempting to create a bidirectional relationship between a PFUser and some PFObjects using PFRelations.
First I create a Posting and save that in the background.
And in the completion block I add the new posting in the PFUser by appending to the list of Postings that are already related to this User and re-save the User with a synchronous save call.
The Posting is always saved correctly with the correct Relation to the User, however, the Relation back to the Posting in the User only works intermittently. Stepping through the code results in the correct outcome each time which leads me to believe it's some sort of race condition. What would cause this behavior? Is there a better way to achieve the desired relationships?
PFUser *currentUser = [PFUser currentUser];
PFObject *posting = [PFObject objectWithClassName:#"Postings"];
__weak HLReviewPostItemViewController *myself = self;
posting[#"title"] = self.createdItem[#"title"];
posting[#"price"] = self.createdItem[#"price"];
posting[#"owner"] = currentUser;
[posting saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
PFGeoPoint *currentGeoPoint = [myself.locationManager getCurrentGeoPoint];
[currentUser setObject:currentGeoPoint forKey:#"geoPoint"];
PFRelation *postingsForUser = [currentUser relationForKey:#"postings"];
[postingsForUser addObject:posting];
NSError *error;
[currentUser save:&error];
if (!error) {
[myself.dataManager fetchLocalUsers];
} else {
NSLog(#"Error saving Posting in user relation: %#", error);
}
} else {
NSLog(#"Error saving Posting: %#", error);
}
}];
When you save the posting, because the posting has a column that stores the owner, the owner - the parent, also gets saved automatically - very nice feature of Parse.
When you step through the code automatic save is executed and finished before your next re-save. But when you run the code, there is no guarantee which save to PFUser object would occur first.
Related
I have a small problem .. I hope you can help me ...
In my app I'm using Parse.com for data management.
I have a ViewController that contains a TextField called "Email".
With a query parse.com call all the registered user app and their email. Now I would like to try to compare the values of the textField and those of the query .. Let me give an example ..
The user enters their email in the textField but if this email is already present in the archive of the users (of course taken by the query parse.com) shows an alert that warns him that the Supplied in textField is already existing in parse.com.
I tried to do this but it does not always recognize the email in query..dove am I doing wrong?
P.S. the textField is not in viewController Main but is in another
ViewController called generalData.
-(void)query {
PFQuery *totalUser = [PFUser query];
[totalUser findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
[array addObject:object];
NSLog(#"%#", [object objectForKey:NPUserKey_EMAIL]);
// NSStrings
email = generalData.emailTextField.text;
compareEmail = [object objectForKey:NPUserKey_EMAIL];
}
}
}];
}
- (IBAction)presentNextViewController:(id)sender {
if ([generalData.emailTextField.text isEqualToString:compareEmail]) {
NSString *stringError = [NSString stringWithFormat:#"L'email %# รจ gia presente nei nostri archivi.",email];
NPUMessageView *alertMessage;
alertMessage= [[NPUMessageView alloc] showViewWithMessage:stringError withBackgroundColor:SECONDARY_COLOR];
[self.view addSubview:alertMessage];
[alertMessage showAnimatedView];
NSLog(#"email found in archive");
}
else {
NSInteger index = [controllersContainer indexOfObject:self.destinationViewController];
index = MIN(index+1, [controllersContainer count]-1);
[self presentCurrentViewController:self.currentViewController withPage:index];
}
}
I think you are getting ahead of yourself a little bit. Parse automatically checks for duplicate emails when you try to sign up a new user. Let the user enter their email into the field, and when they try to create the account, display the error Parse returns from the signup method, and let them try again!
https://www.parse.com/docs/ios_guide#users-signup/iOS
I have a project set up where all data coming from the Server is wrote to a Core Data managed store using a managed model. I have all my entities generated from the Core Data model using mogenerator. I have all RestKit mapping integrated in to my entities.
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"dataModel" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
Now there has been a change of plan due to time constraints. The data should not be stored at all. The data should be read from the server and displayed directly. No saving, no persisting. So I would like to cut out the RKManagedObjectStore, keep the entities and mappings, and read the data from 'RKMappingResult *mappingResult' when a request succeeds or a RKPaginator resutl. Example that works with RKManagedObjectStore and RKPaginator:
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[Friend entityMapping:objectManager.managedObjectStore]
method:RKRequestMethodAny
pathPattern:nil
keyPath:#"items"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
[objectManager setPaginationMapping:[self paginationMapping]];
self.paginator = [objectManager paginatorWithPathPattern:#"data"];
self.paginator.perPage = 20;
//Set completion block for this paginator
[self.paginator setCompletionBlockWithSuccess:^(RKPaginator *paginator, NSArray *objects, NSUInteger page) {
[weakSelf.dataArray addObjectsFromArray:objects];
} failure:^(RKPaginator *paginator, NSError *error) {
}];
However, when I start to reomve the RKManagedObjectStore I start to run into problems when mapping.
'You must provide a managedObjectStore. Invoke mappingForClass:inManagedObjectStore: instead.'
Q.1 Can I use Enitiy Mapping without RKManagedObjectStore? Am I going in the right direction.
Q.2 Can I remove the store and keep the model?
Any tips, help or examples would be great before I get too involved and go in the wrong direction.
Thanks Al
You should fight against the requirement change and use Core Data as a temporary cache of information to aid with memory management (so you can scroll up and down lists without having to have everything loaded all the time). This should not take any longer to implement...
No, you can't use RKEntityMapping without an RKManagedObjectStore.
You could keep the model but you wouldn't be able to use it (managed objects need to be created in association with a MOC).
I am trying to develop a simple app, which, retrieves data from Facebook, when the user connects to it.
After reading Facebook's example about how to retrieve User's photos and User's names, I just want to get information such as gender, city, e-mail, and date of birth, for example.
The following part, is where I got stuck:
- (void)populateUserDetails
{
if (FBSession.activeSession.isOpen) {
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *user,
NSError *error) {
if (!error) {
self.userNameLabel.text = user.name;
self.userProfileImage.profileID = user.id;
}
}];
}
}
My questions are:
Should I make a dictionary with all this data? (gender, city, e-mail, etc.)
-Also, I'm using the storyboard, can I use labels to all those data as Facebook's tutorial states, for the username?
I read in a couple of places that the method requestForMe isn't the appropriate one for the other type of data I am looking for. What would be the method for my requests?
First you must ask user for permissions to access his gender, email, city ...
You make a array with required permissions and add it to the openActiveSessionWithReadPermissions: method
NSArray *permissions = [[NSArray alloc] initWithObjects:#"user_birthday",#"user_hometown",#"user_location",#"email",#"basic_info", nil];
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState status,
NSError *error) {
}];
Then make a request like this to get informations you wanted
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(#"%#", [result objectForKey:#"gender"]);
NSLog(#"%#", [result objectForKey:#"hometown"]);
NSLog(#"%#", [result objectForKey:#"birthday"]);
NSLog(#"%#", [result objectForKey:#"email"]);
}];
I hope i resolved your problem.
I've managed to understand how to incorporate UIManagedDocument into a simple test application and it works as expected! However, now I'm adding support to this basic application so it will work if the user does not want to use iCloud.
So when the URLForUbiquityContainerIdentifier: method returns 'nil', I return the URL of the local documents directory using the suggested method
NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [NSURL fileURLWithPath:documentsDirectoryPath];
However, when I try saving a UIManagedDocument to the local URL (such as: file://localhost/var/mobile/Applications/some-long-identifier/Documents/d.dox) I get the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.'
Using this save method:
if (![[NSFileManager defaultManager] fileExistsAtPath:self.managedDocument.fileURL.path]) {
[self.documentDatabase saveToURL:self.managedDocument.fileURL
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
//
// Add default database stuff here.
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.documentDatabase.managedObjectContext performBlock:^{
[Note newNoteInContext:self.managedDocument.managedObjectContext];
}];
});
} else {
NSLog(#"Error saving %#", self.managedDocument.fileURL.lastPathComponent);
}
}];
}
It turns out my persistent store options contained the keys used for the ubiquitous store. These shouldn't be in the documents persistent store options.
Apologies if this has been answered before but I can't find a reference. I am trying Cocoa / obj-c for the first time. I am trying to knock up an app which will sync with a remote backup system via http (a la s3) and am stumbling around some fundamental core data issues.
I have created an entity and can invoke this without issues. The problem arrives when I call save on NSManagedObjectContext.
I am not going to include all methods involved in invoking the object context / model as the log output should (I think) verify that it is working as expected.
Best described with code and appropriate log entries.
*First, for illustration, I am invoking the managed object: *
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
NSLog(#"The managed object model is defined as follows:\n%#", managedObjectModel);
return managedObjectModel;
}
And the log output from the above NSLog:
2011-09-06 14:31:38.322 TryAgain[18885:a0f] The managed object model is defined as follows:
(<NSManagedObjectModel: 0x2000e2b00>) isEditable 1, entities {
BackupItinerary = "(<NSEntityDescription: 0x20020e9e0>) name BackupItinerary, managedObjectClassName NSManagedObject, renamingIdentifier
BackupItinerary, isAbstract 0, superentity name (null), properties {\n \"file_url\" = \"(<NSAttributeDescription: 0x2000faec0>), name
file_url, isOptional 0, isTransient 0, entity BackupItinerary, renamingIdentifier file_url, validation predicates (\\n), warnings (\\n),
versionHashModifier (null), attributeType 700 , attributeValueClassName NSString, defaultValue (null)\";\n \"last_sync_date\" =
\"(<NSAttributeDescription: 0x2000faf60>), name last_sync_date, isOptional 1, isTransient 0, entity BackupItinerary, renamingIdentifier
last_sync_date, validation predicates (\\n), warnings (\\n), versionHashModifier (null), attributeType 900 , attributeValueClassName
NSDate, defaultValue (null)\";\n}, subentities {\n}, userInfo {\n}, versionHashModifier (null)";
}, fetch request templates {
}
This looks so have been successful. No exceptions thrown, or warnings.
Now, the actual problem arrives when I call save on the object context. I have a NSOpenPanel which allows for picking dir / files to backup (all hooked up and working fine). Upon the user selecting a dir/file I want to set the value, so I:
NSArray *paths = [panel URLs];
NSURL *filePath = [paths objectAtIndex:0];
[directories addObject:filePath];
[directories setArray:[[NSSet setWithArray: directories] allObjects]];
NSEntityDescription *BackupItineraryEntity = [[self.managedObjectModel entitiesByName] objectForKey:#"BackupItinerary"];
NSManagedObject* BackupItinerary = [[NSManagedObject alloc]
initWithEntity:BackupItineraryEntity
insertIntoManagedObjectContext:self.managedObjectContext];
[BackupItinerary setValue:[filePath absoluteString] forKey:#"file_url"];
NSLog(#"entity:\n%#", BackupItinerary);
And the call to NSLog says (having selected /Users/rick/selenium2-webdriver/):
2011-09-06 14:31:38.328 TryAgain[18885:a0f] entity:
<NSManagedObject: 0x200216c80> (entity: BackupItinerary; id: 0x200090860 <x-coredata:///BackupItinerary/t0C005B39-D185-454B-B364-31314EEB10F02> ;
data: {
"file_url" = "file://localhost/Users/rick/selenium2-webdriver/";
"last_sync_date" = nil;
})
So, the file_url seems to be populated then, yes? But when I:
NSError *error;
if (![[self managedObjectContext] save:&error]) {
NSLog(#"Unresolved error %#, %#, %#", error, [error userInfo],[error localizedDescription]);
}
The log says:
2011-09-06 14:31:38.330 TryAgain[18885:a0f] Unresolved error Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x2000cc4e0 "file_url is a required
value.", {
NSLocalizedDescription = "file_url is a required value.";
NSValidationErrorKey = "file_url";
NSValidationErrorObject = "<NSManagedObject: 0x20020f660> (entity: BackupItinerary; id: 0x20008faa0 <x-coredata:///BackupItinerary/t0C005B39
D185-454B-B364-31314EEB10F03> ; data: {\n \"file_url\" = nil;\n \"last_sync_date\" = nil;\n})";
}, file_url is a required value.
So basically:
I can, it seems (?), invoke the an entity and set a value on it, but when it comes to saving it the values is not set. The above code is executed inline and I am using garbage collection.
Feels like a total newb school boy error issue but for the life of me I can't see what I am missing having gone over the docs, highlevel tutorials and example code.
Pointers appreciated!
You are looking at two different managed object instances. The instance that has its file_url property set is <NSManagedObject: 0x200216c80> while the one that reports the error is <NSManagedObject: 0x20020f660>.
Somewhere, you are inserting an instance but not giving it a file_url value. In the code given, it might happen if the user selects cancel in the NSOpenPanel.
Judging from the output of your NSLog's, I'd say that BackupItinerary object is different when saving your context. Check this output on different occasions:
When inserting and setting the value:
entity:
<NSManagedObject: 0x200216c80>
When saving:
NSValidationErrorObject = "<NSManagedObject: 0x20020f660>
As you can see the addresses of instances differ, hence you're inserting one, but saving the other instance. Hope this helps.