Update if data is exist - uitableview

How to update the data if it is already exist in the database, when we click on the button save?
Thanks for your help.
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self.recipe managedObjectContext];
if (!self.material) {
self.material = [NSEntityDescription insertNewObjectForEntityForName:#"Material" inManagedObjectContext:context];
[self.recipe addMaterialsObject:self.material];
self.material.displayOrder = [NSNumber numberWithInteger:self.recipe.materials.count];
}
[self.parentViewController dismissViewControllerAnimated:YES completion:nil];
}

That depends on you how you define instances of your Material entity to be equal. For example, is 100g flour and 200g flour the same (they should be, and you should find a way to separate quantity from ingredient...).
You can implement a method isEqualToMaterial:(Material*)otherMaterial and compare the essential attributes, like name and perhaps category. Before inserting a new entity object you could instead use the existing one.
However, from your code I see that you store a displayOrder in the material object, which depends on the number of materials in the recipe. That would presumably overwrite any existing displayOrder - so your current setup with multiple largely identical materials, while not ideal, might be what you want.
Maybe you should re-think your data model setup and devise a way where the user can choose from a list of ingredients before starting to type in his own.

Related

Single fetchRequest for multiple entities - use relationships and/or entity inheritance?

I have a simple app which uses Core Data. As the user progresses through each ViewController, the managedObject gets passed through each view then saved to the store on completion. At the minute my Model has a single Entity with over 100 properties, which I would now like to separate into multiple entities. I am fairly new to iOS programming and not overly confident with database work, so apologies if my terminology is not correct.
An example of how my app works: On my ClientViewController, I declare managedObjectNGLS which currently stores all attributes.
// Identify the app delegate
NGLSAppDelegate *appDelegate = (NGLSAppDelegate *)[[UIApplication sharedApplication]delegate];
// Use appDelegate object to identify managed object context
NSManagedObjectContext *context = [appDelegate managedObjectContext];
// Create new managed object using the NGLS entity description
NSManagedObject *ManagedObjectNGLS;
ManagedObjectNGLS = [NSEntityDescription insertNewObjectForEntityForName:#"NGLS"
inManagedObjectContext:context];
// Declare managed object
self.managedObjectNGLS = ManagedObjectNGLS;
This gets passed through to ServicesViewController.
// Allocate & initialise ServicesViewController
ServicesViewController *services = [[ServicesViewController alloc]initWithNibName:#"ServicesViewController"
bundle:nil];
// Pass managedObject to view
services.managedObjectNGLS = self.managedObjectNGLS;
// Push next view
[self.navigationController pushViewController:services animated:YES];
Once the user has selected the desired services, the managedObjectNGLS gets stored to my Model.
[[self.managedObjectNGLS managedObjectContext] save:&error];
In the AdminViewController, the user can export the entire contents of the NGLS entity using a fetchRequest. Please see this post on how I am achieving this (apologies if this seems like a duplicate post, but I am still trying to understand all of this).
My question: I would very much like someone to explain the best solution regarding my entity situation. I have tried to set a Parent Entity which seems to work fine when only two entities are involved, but when more are introduced I get lost. I am not sure about relationships or an abstract entity?
Ideally I would like to have separate entities for NGLS (holds various important attributes like date stamp), Admin (username, site location, etc), Client, Employer (possibly separated further into Employer1,...,Employer10 eventually), and Services. I am not sure whether to create an Abstract or Parent entity with no attributes, then just hold relationships to that with the separate entities?
I have laid out what I would expect the entities to look like in the editor, but I am unsure if this is correct. The idea of the app is to store details about one Client per time, so on that basis the Client can have many Employers and many Services. I have implemented an extremely simple "login" system where the lastObject in my Admin entity gets stored in the NGLS entity (see this post for a description), so the Admin entity doesn't necessarily have a relationship with anything (I may be wrong here?).
I have tried the following code but it just crashes the app:
NSMutableArray *entityArray = [NSMutableArray array];
for (NSString *entitySuffix in #[#"NGLS", #"Client", #"Services", #"Employer"]) {
NSString *entitySelect = [NSString stringWithFormat:#"%#", entitySuffix];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entitySelect];
[entityArray addObjectsFromArray:
[context executeFetchRequest:request error:nil]];
}
// CHCSVParser
NSOutputStream *stream = [[NSOutputStream alloc]initToMemory];
CHCSVWriter *writer = [[CHCSVWriter alloc]initWithOutputStream:stream
encoding:NSUTF8StringEncoding
delimiter:','];
// Fetch objects to write to .csv
for (Model *results in entityArray) {
[writer writeLineOfFields:#[results.site,
results.username,
results.dateStamp,
results.clientTitle,
results.clientForename,
// results from different entities
Again, I apologise for this post as it seems like I am asking for an outright answer, but I really just need someone to point me in the right direction on how to set up separate entities and use a single fetchRequest to fetch data from the entire Model with all of it's entities. Thanks in advance!

unwanted objects appearing in core data relationship

Long question---thanks in advance for your time. After saving new managed objects, I am finding them added to a relationship on another object in my core data database---one for which my code calls no setter method and that has no inverse relationship. I have pored over the code and used logs to isolate the occurrence the best I can, but I'm encountering bizarre behavior I cannot explain (or fix).
More specifically:
I have an entity called PendingSyncTracker. It simply has one relationship, objectsToSync. I have not yet added any line in my code to call a setter method on this relationship. It is a to-many relationship. It points to BaseEntity. For the "Inverse" option, I have selected "No Inverse Relationship."
When I load a particular table view, 3 objects are downloaded from a server and then parsed into managed objects and saved. By the time the table view begins loading cells, 2 of those 3 objects will mystifyingly be present in the objectsToSync relationship.
I have used NSLog all over my code to figure out exactly when these objects can first be found as members of the objectsToSync set.
NSSet *objectsToSync = [[[SyncEngine sharedEngine] fetchClassNamed:#"PendingSyncTracker" withPredicates:nil][0] valueForKey:#"objectsPendingSync"];
NSLog(#"PendingSyncTracker objectsToSync set (%lu objects): %#", (unsigned long)[objectsToSync count], objectsToSync);
The answer to when they first appear in the set actually varies depending on where I do/don't place those 2 lines of code!
The objects are never found on the relationship before the managed object context is saved in the course of saving my 3 new core data objects.
If I don't use those 2 lines till I'm back in the Table View Controller that sent the new objects off to the Sync Engine to be stored locally (where the MOC is accessed and saved), then the log will there reveal that 2 objects have been added to the relationship.
If I use those 2 lines immediately after saving the MOC in the Sync Engine, then the logs will indicate (both there and back in the TVC) that only 1 object has been added to the relationship.
If I use those 2 lines immediately before and after saving the MOC (and back in the TVC), then all 3 logs will reveal that the relationship contains an empty set.
I also have those 2 lines at the beginning of cellForRowAtIndexPath. Regardless of prior logs, that log will always indicate that 2 objects have been added to the relationship.
All 3 of the managed objects that are created in the Sync Engine are stored as entity types that are subEntities of BaseEntity (to which the objectsToSync relationship points). The 2 types that get added to the relationship are each defined to have a reciprocal relationship, but with a different object, not PendingSyncTracker (although the different object is a subEntity of BaseEntity!).
So.. what explains these observations? How are these objects getting added to the relationship?
UPDATE:
- (NSArray*) fetchClassNamed:(NSString*)className withPredicates:(id)parameters;
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// set predicates
if (!(parameters == nil)) {
[fetchRequest setPredicate:parameters];
}
NSError *error;
NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
return fetchedResults;
}
First, what does [[[SyncEngine sharedEngine] fetchClassNamed... do? Just a guess but it is doing something with KVC to set the relationship for you.
Also, you should always, always, always have an inverse relationship. Even if you never use it, Core Data does. Not having an inverse can lead to lots of issues, including but not limited to performance problems and potentially data corruption.
Add an inverse relationship and update your question with what -fetchClassNamed... does.

Adding a relationship in core data

I have been at this single task for several days trying to get the relationships between core data entities working. I have achieved this but now I need to change it so that the new attribute value has its relationship added to an existing object. It is a 1 - to - many database.
I am not sure how to add a relationship to a object that already exists. So in the new object that is getting added to RoutineDetail, how would I create the relationship to the object that already exists in the routine Entity?
I have looked at several examples all showing how to add relationships to newly added objects but I need it so the new object in RoutinesDetails has a relationship with the value that already exists in Routines.
The value of Routines is held in a string called RoutineText
rout is the NSmangedObject for the entity Routines
routDet is the NSmanagedObject for the entity RoutinesDetails
I have left the commented out code that allows me to add a relationship when both new objects are created.
This is the last thing I have to do in my project but it is driving me insane. I will be eternally grateful for the fix here. Any advice will be appreciated as this is the best knowledge portal there is. Thank You.
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new device
ExcerciseInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];
//rout = [NSEntityDescription insertNewObjectForEntityForName:#"Routines" inManagedObjectContext:context];
routdet = [NSEntityDescription insertNewObjectForEntityForName:#"RoutinesDetails" inManagedObjectContext:context];
//Add attribute values
//[rout setValue: RoutineText forKey:#"routinename"];
[routdet setValue: info.name forKey:#"image"];
//Create Relationship
[rout addRoutinedetObject:routdet];
Your main problem statement is, I think, here:
I need it so the new object in RoutinesDetails has a relationship with the value that already exists in Routines.
I presume your data model looks like this:
Routine <----> RoutineDetail
i.e. every one routine has one routine detail (one-to-one relationship).
But this does not really make any sense. You could simply include the attributes of RoutineDetail in the Routine entity.
Instead of
desiredValue = routineDetail.routine.value;
you would simply have
desiredValue = routineDetail.value;
Also, please note that your code has a number of problems. The first line is completely unnecessary, just use self.managedObjectContext. Additionally, against the convention you are using Capital initials for variables. Those should be reserved for class names. Your method to add the relationship also does not look right.
You can add a relationship like this, without a method call:
routineObject.detail = detailObject;

How to add attributes to an existing Core Data entity (object) programmatically?

I would like to add attributes programmatically to an entity during runtime of my app.
Is this something you would recommend doing or can this lead to issues?
How would I need to combine NSAttributeDescription and NSEntityDescription? I am familiar with creating models using Xcode, but did not do it using NSEntityDescription yet.
It's theoretically possible, but doesn't appear very practical.
You can modify the NSManagedObjectModel programmatically, as well as NSEntityDescription. Note that -setEntities: (NSManagedObjectModel) and -setProperties: (NSEntityDescription) both trigger exceptions if you modify a model that has been instantiated. So you can't modify your existing model's structure. You'd have to create a new one and copy all of your data from the old Core Data stack to the new one based on your new model..
Using NSMutableDictionary is a much saner approach.
This is an article talking about this in great detail. Hope it helps.
I would not do this. If the store becomes incompatible with your model it will just crash. Is this risk really worth the benefit you are trying to create?
I have found that it makes sense to create more (even many more) attributes upfront just "to be on the safe side". The overhead of unused attributes is really minimal, but you get the flexibility of easily adding information to your objects "on the fly".
As pointed out in the comments, one good way to implement that is using a separate entity for attributes and adding them as to-many relationships.
I just used nearly the same technique here: EPPZQueuedObject.h
Although, I think mutate the entity architecture during runtime could lead to incompatiblity issues (an exception actually), when the stored SQLite data won't fit for your initial entities at startup.
So this generic object EPPZQueuedObject is an object of two attributes at all, so I had no intention to use a separate model file only for this purpose. But this structure is not mutating during runtime.
#implementation EPPZQueuedObject
#dynamic creationDate;
#dynamic archivedObject;
+(NSEntityDescription*)entityDescription
{
//Describe EPPZQueuedObject.
NSEntityDescription *entityDescription = [NSEntityDescription new];
entityDescription.name = EPPZQueuedObjectEntityName;
entityDescription.managedObjectClassName = NSStringFromClass(self);
//Describe creationDate.
NSAttributeDescription *creationDateDescription = [NSAttributeDescription new];
creationDateDescription.name = #"creationDate";
creationDateDescription.attributeType = NSDateAttributeType;
creationDateDescription.attributeValueClassName = #"NSDate";
creationDateDescription.defaultValue = nil;
//Describe archivedObject.
NSAttributeDescription *archivedObjectDescription = [NSAttributeDescription new];
archivedObjectDescription.name = #"archivedObject";
archivedObjectDescription.attributeType = NSBinaryDataAttributeType;
archivedObjectDescription.attributeValueClassName = #"NSData";
archivedObjectDescription.defaultValue = nil;
//Add attributes.
entityDescription.properties = #[ creationDateDescription, archivedObjectDescription ];
//Voila.
return entityDescription;
}
#end
More details in the corresponding article: http://eppz.eu/blog/simple-core-data-sample/
I'm working on something similar and I'm thinking about creating a new core data class called "Properties", so I can set my core data objects to have a "relationship to many Properties". Each Property would have core data string-type attributes: "attribute", "type" and "value".
I think that should give enough flexibility to add properties to a core data object in the fly. If I happen to implement this, I will post it here

Not getting To-Many Relationship in Core Data

I am trying to understand Core Data (To-Many) relationship. In the following code, I have two Entities
PeopleList <-->> TransactionDetails
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *personDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"PeopleList"
inManagedObjectContext:context];
[personDetails setValue:[person fullName] forKey:#"name"];
NSManagedObject *transactionDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"TransactionDetails"
inManagedObjectContext:context];
[transactionDetails setValue:[NSNumber numberWithFloat:oweAmount] forKey:#"amount"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
Now this code inserts a New Object (row) to the model. What I am confused with is:
1. Don't I have to write code for relating object values in two Entities (PeopleList and TransactionDetails)?
2. If I run this code again and again, It just keep on adding same object in first Entity (PeopleList). How to write for To-Many relationship? What I can get from last few hours of reading is I have to fetch the results, search for that particular object and if it exist then dont insert a new object with same name. But in that case, how will it relate the two entities.
Are your model entities correctly wired in the model editor as far as the to-many relationship is concerned? Have you generated the class files for your entities? If you can answer both questions with yes you create a personlist entity as you did and the details entity too but you need to the details to your personlist. Have a look into the class files for the method name(s).
It won't, because you're not setting the relationships on either of your objects. I don't see where you're setting the PeopleList property of your newly minted TransactionDetail object (sorry, I don't know how you've got the properties named in your model, so I'm just using the class names). So, after creating your transactionDetails object, you'd need to do something like transactionDetails.PeopleList = personDetails, and both relationships would be set at that point; transactionDetails.PeopleList property would point to your personDetails object, and personDetails.TransactionDetails set would contain transactionDetails.
What is your person object, that you're using to set the name from?
On another note, you might want to consider moving all this sort of stuff into subclasses of NSManagedObject; write your own super easy constructors/initializers, etc, for each of your entities. Lots of people never do this, and end up littering their controller code with lots of CoreData boiler plate, which is a mystery to me, because it's what makes using CoreData so nice.

Resources