Programmatically update duplicate values in coredata? - ios

I am new in Core Data. I want to update duplicate values. For example my table looks like this
id | Name
============
1 | Joseph
2 | Fernandez
3 | Joseph
4 | James
Say that I want to update Joseph corresponding to id 1 and 4 to "myName". When I tried to update this it only updates the 4th row. I can't find any way to do this in any of the documentation. Can anyone suggest me a solution?
One more question, how can I print all name values?

you will have to read over the documentation to know how to update record
http://www.appcoda.com/core-data-tutorial-update-delete/

James,
I'll try to reply to both your questions with sample code.
To update specific objects you need to se up a new NSFetchRequest with a predicate, grab the objects (of type NSManagedObject), update the values you are interested in and save the context.
So, for example:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"YourEntityName"];
// set the predicate (it's equal to set a WHERE SQL clause) filtering on the name for example
// use camel case notation if possible, so instead of Name use name (for this you have to changes your model, if you don't want to do it use Name)
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"name == %#", #"Joseph"]];
NSError* error = nil;
NSArray* results = [context executeFetchRequest:fetchRequest error:&error];
// do some error checking here...
for (NSManagedObject resultItem in results) {
// use KVC (for example) to access your object properties
[resultItem setValue:#"myName" forKey:#"name"];
}
// save your context here
// if you don't save, changes are not stored
To print you need to se up a new NSFetchRequest, grab the objects (of type NSManagedObject) and use NSLog.
So for example:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"YourEntityName"];
NSError* error = nil;
NSArray* results = [context executeFetchRequest:fetchRequest error:&error];
// do some error checking here...
for (NSManagedObject resultItem in results) {
NSLog(#"%#", [resultItem valueForKey:#"name"]);
}
P.S. The code I provided is quite simple and the predicate I used to specific values check against the name. Since this could be error prone, I would modify the model and using a sort of guid for each objects you need to use (I don't know if id is for that but I would change its name to another one, for example userId). Once done you can check against it.
Hope that helps.

It's as simple as retrieving the NSManagedObject and changing the Name property. You can retrieve the NSManagedObject with a fetch request. Once you changed the property and you want to keep it changed even when you close the application you'll have to do a save on the managedObjectContext.
You'll have to read over the documentation to get up to speed on core data:
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdBasics.html#//apple_ref/doc/uid/TP40001650-TP1
Edit: just NSLog whatever you want to know, for example log you fetch request results.

Related

Using Kii Query Result

I am running a Kii Query that returns the expected number of results. However the results array contains object formatted as follow
"<KiiObject: 0x130471ae0>"
this is the output from
NSLog(#"%#",results);
I am confident that the Query is working correctly as i can add and remove objects from the bucket and the number of results in the array changes according, i just don't know how to take the results and get the corresponding object.
I have gone over everything in the Kii Doc's
link to Kii Docs
The result is to short to be the object id(uuid string) and i can't find any other reference in the docs that makes sense.
You can refer to bellow snippet
NSError *error = nil;
// Build "all" query
KiiQuery *allQuery = [KiiQuery queryWithClause:nil];
// Create an array to store all the results in
NSMutableArray *allResults = [NSMutableArray array];
// Create a placeholder for any paginated queries
KiiQuery *nextQuery;
// Get an array of KiiObjects by querying the bucket
NSArray *results = [bucket executeQuerySynchronous:allQuery
withError:&error
andNext:&nextQuery];
if (error != nil) {
// Error handling
return;
}
//obtain single KiiObject
KiiObject* firstObject = allResults.firstObject; // now you should get all the object properties
NSLog(#"MaxScore : %#",[firstObject getObjectForKey:#"maxScore"]); //i.e to get "maxScore" value
Below is the links for querying KiiObjects.
http://docs.kii.com/en/guides/ios/managing-data/object-storages/querying/

MagicalRecord: one to many relationship

I m not quite sure if it is the right approach
I want to have a one to many relationship between entity A and B, so A could refer to multiple records of B, but B only to one record of A
my question is how to do this with MagicalRecords... i m familiar with the basic fetching and creating of single entities
but have no clue how to fetch, creat, update entities with realtionships
First of all, you need to beware of the 'context' of CoreData. I usually use [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {}]; method, and create entity inside the block.
I will use my case i show above as an example,
To save a "message" to a "conversation" entity,
ChatConversation* chatConversation = [ChatConversation MR_findFirstByAttribute:#"conversationID" withValue:<your value> inContext:localContext];
^first see if there is a entity already been created using the find first by attribute method
ChatMessage* chatMessage = [ChatMessage MR_createInContext:localContext];
[chatMessage set.....];
......
if(!chatConversation){
//You need to create a new chatConversation entity
chatConversation = [ChatConversation MR_createInContext:localContext];
......
}
[chatMessage setChatConversation:chatConversation];
If you want to get all message from a chat conversation, just fetchAll messages with custom predicates
-(NSFetchedResultsController *)fetchedResultsController {
if(!_fetchedResultsController)
_fetchedResultsController = [ChatMessage MR_fetchAllSortedBy:#"createdAt" ascending:TRUE withPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"conversation.conversationID",self.conversationObject.conversationID] groupBy:nil delegate:self];
return _fetchedResultsController;
}
What do you mean by "how to do this with MagicalRecords"? Cause this is not handle by MagicalRecords.
They are all set in the coredata model file.
For instance, for my project, i am implementing the IM feature.
1. One conversation -> many messages
2. One message ->one conversation
this is the design of data model.
feel free to ask me any questions about Coredata + MR
Cheers!

iOS Core Data <fault after searching

All, I am having problems with Core Data.
I have a method that queries all data which matches a jobId
- (JobSummary*)summaryForJobId:(NSInteger)jobId {
NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:[JobSummary entityName]];
request.predicate = [NSPredicate predicateWithFormat:#"jobId = %D", jobId];
JobSummary* summary = [[self.context executeFetchRequest:request error:nil] lastObject];
NSLog(#"DB Summary: %#", summary);
[request setReturnsObjectsAsFaults:NO];
return summary;
}
When called and I log out it works perfect, however when I call it from a seperate view controller like so;
JobSummary *retrievedDictionary = [[FSScheduleDatabaseTransaction new] summaryForJobId:jobid];
When I log out retrievedDictionary it spits out this;
<JobSummary: 0x12de24a0> (entity: JobSummary; id: 0xb3c19b0 <x-coredata://7E9F6C6E-B4A0-4450-8905-184C6C8FB60D/JobSummary/p169> ; data: <fault>)
Any help much appreciated!
It is giving you the log correctly. When ever you try to print the ManagedObject you'll only see a fault in logs.
A fault is a placeholder object that represents a managed object that has not yet been fully realized, or a collection object that represents a relationship:
A managed object fault is an instance of the appropriate class, but its persistent variables are not yet initialized.
A relationship fault is a subclass of the collection class that represents the relationship.
So, in short, unless you try to access those properties, CoreData wont fill property values. It will have a fault. Just trying to log the ManagedObject, without accessing its properties previously, will only show fault.

How to get data from unrelated entities using Core Data?

I have these three entities which are not related. How can i get the salary of an employee. I want something like this :
Select C.salary FROM Employee A, Department B, Salaries C Where A.id=B.empid AND B.id=C.deptid AND A.id=12
i need to do the same above operation in core data.
As Martin suggested, the easy way is to set up the needed relationships and traverse them.
But if you don't have the permission to alter the model, you need to work and filter managed in memory object you own once retrieved. In other words, you need to set up NSFetchRequests with the correct NSPredicates.
For example, to retrieve the department for a given employee.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:#"Department"];
request.predicate = [NSPredicate predicateWithFormat:#"empid", #(12)];
NSError *error = nil;
NSArray *departments = [moc executeFetchRequest:request error:&error];
if(departments) {
// do a NSLog for departments here to see what you have
// here you can access deptid for each department you retrieve
// and with that value run another request against Salaries
// e.g. NSManagedObject* department = [departments lastObject];
// NSNumber* deptid = [department valueForKey:#"deptid"]
} else {
// handle error here
}
Other part is left as excersise ;)

Fetch request returns old data

I'm getting outdated results when executing a fetch request upon a NSManagedObjectContextObjectsDidChangeNotification.
As an example, consider directories with documents that can be deleted logically with a boolean attribute (softDeleted). The fetch request should return all directories that have at least one non-deleted document (directoriesWithDocuments).
The initial state is single directory with a single deleted document. directoriesWithDocuments returns an empty array.
The following code restores the document by setting the softDeleted boolean to NO.
[_context.undoManager beginUndoGrouping];
[_context.undoManager setActionName:#"undelete"];
document.softDeleted = #(NO);
NSError *error;
BOOL success = [_context save:&error]; // This triggers the notification
[_context.undoManager endUndoGrouping];
The save triggers a NSManagedObjectContextObjectsDidChangeNotification. I expected directoriesWithDocuments to return the directory, but instead it still returns an empty array.
- (void)objectsDidChangeNotification:(NSNotification*)notification
{
NSArray *objects = [self directoriesWithDocuments]; // Still empty!
}
Yet, if I execute directoriesWithDocuments after saving the context, either right away or in the next run loop, it returns the directory as expected.
This is the code of the fetch request:
- (NSArray*)directoriesWithDocuments
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY documents.softDeleted == NO"];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Directory"];
fetchRequest.predicate = predicate;
fetchRequest.includesPendingChanges = YES; // Just in case
NSError *error = nil;
NSArray *objects = [_context executeFetchRequest:fetchRequest error:&error];
return directories;
}
I suspect that the context has some kind of cache for fetch requests that is not cleared until the notification is handled. Is this how Core Data is expected to behave? Or am I doing something wrong?
Workaround
I'm currently delaying the execution of the fetch request like this:
- (void)objectsDidChangeNotification:(NSNotification*)notification
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// HACK: Give time to Core Data to process pending changes or invalidate caches
NSArray *objects = [self directoriesWithDocuments]; // Returns the directory as expected
}];
}
Experiments
Per #MikePollard's suggestion, I checked the returned value of directoriesWithDocuments in NSManagedObjectContextWillSaveNotification and NSManagedObjectContextDidSaveNotification. The results are (in order):
NSManagedObjectContextObjectsDidChangeNotification: empty (wrong)
NSManagedObjectContextWillSaveNotification: empty (wrong)
NSManagedObjectContextDidSaveNotification: 1 directory (correct)
First of all, it looks like you have a boolean attribute called "deleted" defined in your model.
I recall that doing so can be a significant problem because it conflicts (at a KVC level) with NSManagedObject isDeleted. You might want to change that just to make sure it's not the culprit.
Edit
Thanks for replying. I used deleted as a simple example; it's not the
actual attribute I'm using. Will change it to softDeleted to avoid
confusion
I've updated my suggestion below to match the softDeleted in your example.
That said, I think what's at work here boils down to the question of what constitutes a "change" for includesPendingChanges = YES.
Before the context has completed its save, the only 'change' is to Document entities, not Directory entities.
So when the fetch request includes pending changes, there are no Directory entities with any pending changes so you end up with the previous results.
To test that theory, give this a shot:
[_context.undoManager beginUndoGrouping];
[_context.undoManager setActionName:#"delete"];
document.softDeleted = #(NO);
[document.directory willChangeValueForKey:#"documents"] // any attribute will do really
[document.directory didChangeValueForKey:#"documents"]
NSError *error;
BOOL success = [_context save:&error];
[_context.undoManager endUndoGrouping];
What you're doing with the fake will/did changeValueForKey is to "dirty" the associated Directory object. My guess is that it will then be considered "changed" and, as such, included the fetch results.
It strikes me that the predicate is going to be translated into a SQL statement so until the save hits the DB you're not going to get the result you want ... (Not that'd you'd image that from just reading the NSFetchRequest documentation.)
So try doing the fetch as a result of the NSManagedObjectContextDidSaveNotification.
I bet if you turn on -com.apple.CoreData.SQLDebug 1 you'll see the SQL statement.

Resources