I am inserting values in database using core-data and see that duplicates are also allowed.
I have searched got that i have to fetch the contents first and then match with the current data if not match then insert but it is not seems the feasible solution.
So please give some alternate.
That is correct. Core Data provides an API for managing an object graph. It also provides the ability to persist the object graph.
The programmer must guarantee uniqueness of objects, if such uniqueness is desired.
The usual approach is to have a key field for each object. You can then search for an object with the same key-field value. If such an object is found, you can update the information in that object. If such an object is not found, you can create a new object.
In any case, you, the programmer, are responsible for guaranteeing uniqueness. There are many ways to do this.
Most object representations have an inherent key-field. If your key is the entire object state, then maybe you can add a unique key to the object layout... or use a cryptographic hash of the entire object as a key field.
Try the following
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MyEntityName"];
[request setPredicate:[NSPredicate predicateWithFormat:#"keyToBeCompared = #"DuplicateString"]];
NSError *error = nil;
MyEntityName *objectInstance = [[self.managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (objectInstance && !error) {
NSLog(#"duplicate object found");
// Jst update the keyValue
objectInstance.keyToBeCompared = #"New Updated value"; // This will automatically update the duplicate value
}
Related
I need to check if there any nil values in my managedObjectContext before saving them. At the minute I am doing tests on each key using if statements. I have many attributes in my NGLS entity, so as you can see testing each key for nil values doesn't seem very practical.
if ([_managedObjectNGLS valueForKey:#"q1"] == nil){
[_managedObjectNGLS setValue:#"" forKey:#"q1"];
}
if ([_managedObjectNGLS valueForKey:#"q1More"] == nil){
[_managedObjectNGLS setValue:#"" forKey:#"q1More"];
}
....
// Save managedObject
NSError *error;
[[self.managedObjectNGLS managedObjectContext] save:&error];
NSLog(#"%#", self.managedObjectNGLS);
NSLog(#"Data saved");
Often there are many nil values, which I need to change manually with many if statements. Is there a way to batch test all objects in my entity for nil values? And if so, is there a way to replace all of these with a set string at once? Thanks.
Core Data includes powerful capabilities for validation. As part of a save, Core Data validates object properties, ensuring that only valid object are persisted.
You can implement validation rules in the Core Data Model Editor, or yourself in code by implementing key-value validation on your managed object subclass.
For example, using the model editor you can set a property to be non-optional and provide a default value:
I have a simple to do list application. It uses dynamically generated text fields spaced programmatically for the tasks (I didn't use UITableView because of some custom animations and whatnot that I want to use).
In the app the user can create multiple lists (home, work, school, etc.) each with their own tasks.
I want to use Core Data to store the information
Saving the information is straightforward enough but updating my Core Data objects (List and Task) are where I'm getting stuck. Also how, with Core Data, to associate in a specific tasks with a specific list.
let me know if I need to clarify anything.
Your best bet is NSFetchedResultsController. You can use it exactly like in the pattern suggested by the Xcode templates (you can look at it by creating a new project Master-Detail and checking "User Core Data").
You can device your object model (entity Task) with a string attribute for the name as well as a NSNumber boolean attribute for done etc. I also recommend a timestamp and maybe a serial number for ordering it (I find NSOrderedSet unreliable). Your entity List should have a to-many relationship to Task. Pretty straight forward.
List <---->> Task
The only difference is now to find the right object, because you cannot use objectAtIndexPath. You can use the index in fetchedResultsController.fetchedObjects for that. Just make sure your objects are ordered as expected.
I'm not totally clear on your question, however, the task of updating a managed object is straightforward. When you're doing an initial add (similar to an "insert" in SQL) you might use code like this:
NSManagedObject *obj;
obj = [NSEntityDescription insertNewObjectForEntityForName:#"UserData" inManagedObjectContext:context];
[obj setValue:user forKey:#"userName"];
[obj setValue:goalCategory forKey:#"goalCategory"];
[obj setValue:goalDetail forKey:#"goalDetail"];
NSError __autoreleasing error;
[context save:&error];
That's about it for inserting a new item. For updating after you're found the managed object you're working on, you just change the values in the managed object and use [context save:&error]. Here's an example:
UserData *uData = (UserData *) managedObj;
uData.itemName = nameText;
NSError __autoreleasing *error;
[context save:&error];
That's pretty much it.
As to the update, once you have selected the object(s) to be updated, they are contained in
fetchedResultsController.fetchedObjects
which is an NSArray. So, you might do something like this:
UserData *uData = (UserData *) [fetchedResultsController.fetchedObjects objectAtIndex:3];
uData.completed = YES;
NSError __autoreleasing *error;
[context save:&error];
So, this would update the field completed in the UserData entity to be == YES for the object at index 3 in the fetchedObjects array.
I know there are other methods of updating and lots of options but I haven't found any need for them. fetchedObjects is an array containing the items returned by your fetch; to update them, cast each object to the entity (which is defined as a NSManagedObject), make the change then context save..
HTH.
First of all, think is it good idea to use Core Data for your project. If your model is light and simple, maybe it will be better to use plists.
If you choose Core Data, just remember 2 rules:
Each thread owns separate NSManagedObjectContext;
Perform operations with context only in its thread.
And don't worry about optimizations now. Realize any scheme of updating your storage. Make sure it works. And then you should try some other update methods.
I am attempting to get an object from a fetch request, and I am checking a property of that object.
Depending on what type the property is will cause me to display a notification or not. After making a successful connection, I set the property type of my object to 'updated' from 'inserted.' Then, when I refresh my view, I pull all objects from coredata and check their properties for the 'updated' type. The problem I am having is that the objects returned in my fetch request that I just attempted to change to 'updated' still display the old 'inserted' value from the fetch request, but don't immediately after the submission. Its like they are reverting. (and I AM saving the context)
What is even more confusing is I have gotten a program to look at the actual tables in the database file stored on the device, and it actually shows the correct value of updated in the table. But the fetch request still comes back with the object having incorrect data. And no amount of refreshing fixes the issue.
How can a fetch request be giving me objects with old/incorrect data when the coredata file shows the tables with correct values?
// code for the fetch request
// return an array of all assets for a specific customer
NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:#"Asset"];
fetchReq.predicate = [NSPredicate predicateWithFormat:#"customerID = %#" argumentArray:[NSArray arrayWithObject:customerID]];
NSArray *results = [[CoreDataManager sharedManager] executeFetchRequest:fetchReq];
return results;
//executeFetchRequest method
NSManagedObjectContext *context = [self managedObjectContextForCurrentThread];
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
return results;
#Arcanfel's suggestion (in comments to the original question) certainly helped me find the solution to my own problem, but it was hidden in the 'Show more comments' section.
It would have been helpful to see that as an answer, not a comment, so I've taken his comment and expanded it a little.
Original comment:
[self managedObjectContextForCurrentThread] - I think this can cause a
problem, as different contexts can have different version of
NSManagedObjects. You can call [fetchReq
setShouldRefreshRefetchedObjects:YES] and it will return the most
up-to-date version of your objects
Whilst using the setShouldRefreshRefetchedObjects:YES didn't resolve my problem of subsequent fetch requests not bringing back the right data, it did make me look at my context management.
In one method I was setting the context, and then reading data. In another method I was setting another context, reading data, altering it and then saving that context... a different context to what my first method had.
In short, be careful that you're referring to the same context or anything you update in context1 won't be in synch with what you retrieve from context2.
I have two versioned Data Models in my iOS project.
The difference between the Data Models is that newer model has a new field named "new_col".
In some part of the project, I need to fetch data filtered by this "new_col" field.
So, I used the following codes.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"new_col = %#", val];
[request setPredicate:predicate];
NSArray *results = [context executeFetchRequest:request error:&err];
And I faced the exception - 'NSInvalidArgumentException', reason: 'keypath new_col not found in entity ...
I know that it's because of the original data model.
I think that I should convert the original data to the new data model.
How can I convert?
If you are adding new attributes and want to maintain backwards compatibility you should specify a default value for the new attribute, or leave it as "optional" in the core data editor.
Once you have done this, you need to make sure automatic migration is enabled. See Implementation of “Automatic Lightweight Migration” for Core Data for details on how to do this. Existing/old entities will then take on the default value (if defined) or nil if the attribute is optional.
Quick Google.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
This is supported by Apple and is the correct way to migrate your users data from one model to another.
I am trying to map a relationship between Articles and the Photos that belong to them using RestKit. Both objects get stored properly when requesting the resource, but it seems the relationship does not persist. In fact, the Article model seems to not even respond to the Photos selector (This may be the 'duh' spot, but I will provide full code to be through).
I've provided all code in a gist, as I find it easier to look through and format then on StackOverflow. Sorry if this is actually an inconvenience.
https://gist.github.com/3733334
And here is the image of the core data model and the relationships set up (sorry, I had to combine them since I can only post 2 hyperlinks currently):
http://imageshack.us/a/img33/5039/stackoverflowissue.jpg
Everything seems to be working properlly except the relationship between the objects when I try to access photos via anArticle.photos. The selector is unrecognized. I set up a convience method in the Photo model to return all photos with a matching article ID, but I feel this is an un-ideal solution as it sort of removes the whole idea of creating a relationship.
I feel there may be something simple I am missing and any help would be greatly appreciated.
Thanks!
So of course it was a "Duh" error. After some help from a local developer, he pointed out that my Photos variable in my Article.h file was an NSArray, and needed to be changed to an NSSet to store objects mapped by RestKit.
Theres some inconsistency between different versions of RestKit. If you are using the latest one mappings should be set up as shown here: https://github.com/RestKit/RestKit/wiki/Object-mapping. If you want to use entity classes for model specific methods make categories on your NSManagedObjects so that when you change your data model you can regenerate them (Do this only after you extract your methods to a category! Select an entity in your .xcdatamodeld and go to Editor -> Create NSManagedObject Subclass...).
I moved my mappings to the controller that is responsible for syncing with the remote API.
This shuld be helpful too: http://andriyadi.me/logging-in-restkit/.
Also Core Data guidelines stress that you should set up inverse relations (but it's not obligatory).
Fetching entities can also be done better in my opinion. In my project I have an NSObject subclass singleton that (among some other Core Data convenience functionalities) fetches by entity and predicate:
- (NSArray *)fetchEntities:(NSString *)entity usingPredicate:(NSPredicate *)predicate {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error) {
RLog(#"Error fetching entity %# using predicate %#", entity, predicate);
abort();
}
if ([results count] >= 1) {
return results;
}
return nil;
}
You can alter it to pass predicates as NSStrings too.
Side note:
Currently I'm also struggling with RestKit and object mapping myself :).