core data - insert new object with two entities - ios

I have two entities with a to-many relationship, there can be multiple items to each list.
List
Item
Basically all I want to achieve is to to save an Item to a specific List on click.
I've figured out how to save a new list:
- (IBAction)handleBtnAddList:(id)sender
{ MyListAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newList;
newList = [NSEntityDescription
insertNewObjectForEntityForName:#"List"
inManagedObjectContext:context];
[newContact setValue:#"Shopping" forKey:#"name"];
NSError *error;
[context save:&error]; }
But how do I save an item to the newly created list "Shopping" ?
Any help appreciated. Thanks in advance!
Hi there, finally had a chance to try this out. It's working now but running into another issue. I can add a new list, but can't seem to add an item.
I looked in the sqlite database and under the 'Item' entity the column named 'zLists' is empty. How do I get a value in there that will correspond with the 'List' that the item should be under?
This is what I've got
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *list = [NSEntityDescription
insertNewObjectForEntityForName:#"List"
inManagedObjectContext:context];
[list setValue:#"Test List" forKey:#"name"];
NSManagedObject *item = [NSEntityDescription
insertNewObjectForEntityForName:#"Item"
inManagedObjectContext:context];
[item setValue:#"Test Item" forKey:#"name"];
I also tried adding this at the end but it crashes the app
[item setValue:#"Test List" forKey:#"lists"];

For my example to work with your code, you will need these prerequisites...
You have a Core Data model NSManagedObjectModel,
Within that model, you have an entity List,
Within that model and for that entity, you have two attributes listDate, and listText,
You have used Xcode to prepare (or have manually prepared) an NSManagedObject subclass for your entity List.
Instead of your line of code: [newContact setValue:#"Shopping" forKey:#"name"];...
you might choose to use dot notation to set attribute values in this manner...
newList.listDate = [NSDate date]; //sets the attribute to current date and time
newList.listText = <<some user input from (for example) a view controller>>;
or to match your syntax, you might choose to use key-value coding in this manner.
[newList setValue:[NSDate date] forKey:#"listDate"];
[newList setValue:<<some user input>> forKey:#"listText"];

Related

Save to more than one entity

For a detail view I would like to let the user leave notes for each item. The app is for a data-driven website. In the web version, the web app stores notes in a separate table with a field for the itemid.
In Core Data I have an entity of items and another entity of notes. The notes entity has an attribute called itemid. When user creates a note the first time, it stores the itemid in the note record.
My question is when you pull up the item for editing how can you simultaneously pull up the right note based on the note having a certain itemid?
In a database situation you could do a join, or from a web page you could make two separate requests to the two tables but I am somewhat flummoxed by how to do this with Core Data.
Do you have to put a relationship to the note and therefore have the noteid in the item row?
If so would you be able to access the note attribute using the item object?
Thanks in advance for any suggestions.
This is what I am using to save information. I just don't know how to make sure I'm saving it for right note.
self.managedObjectContext = [IDModel sharedInstance].managedObjectContext;
NSString *noteText = _notesView.text;
NSNumber *itemId = self.item.itemid;
// Populate Record
[self.note setValue:noteText forKey:#"note"];
[self.note setValue:itemId forKey:#"itemid"];
Model (simplified):
Item:
name NSString
itemid: Integer 64
Note:
note NSString
noteid: Integer 64
itemid: Integer 64
Edit:
Code to try to link note and item while creating both...
//in save method
// Create Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Notes" inManagedObjectContext:self.managedObjectContext];
// Initialize New Record ie newNote
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
// Populate Record
[record setValue:note forKey:#"note"];
[record setValue:localid forKey:#"localnid"];
// Save Record
NSError *error = nil;
if ([self.managedObjectContext save:&error]) {
// If note saved, save new item…
if (itemlength>1) {
Items *newItem = [NSEntityDescription insertNewObjectForEntityForName:#“Item” inManagedObjectContext:self.managedObjectContext];
newItem.item = item;
newItem.note = self.note
//This is null as note does not seem to pick up newly created note.
if (![self.managedObjectContext save:&error]) {
NSLog(#"Error: %#", [error localizedDescription]);
}
}
Yes, you should use a relationship between the Item and Note entities. To create a relationship just Ctrl-drag from one entity to the other in the data model editor. Note that Xcode automatically adds an inverse relationship:
I've renamed the relationships for clarity - you can tailor the details of the relationship (name, one-one v one-many, delete rule, etc) in the panel on the right. In the example above, the Item entity has three properties: 2 attributes and 1 relationship. Given an Item object, say myItem, the values for these properties can be accessed using the Key Value Coding methods: valueForKey: and setValue:forKey:. For example, if attribute is defined as a string:
NSString *myStringValue = [myItem valueForKey:#"attribute"];
[myItem setValue:#"new value for attribute" forKey:#"attribute"];
That's very long-winded. So to make life easier, use the "Create NSManagedObject subclass..." option. Xcode will configure each entity to be a subclass of NSManagedObject and will create new class files (.h/.m or .swift) with details of the properties. For the example Item:
#property (nullable, nonatomic, retain) NSString *attribute;
#property (nullable, nonatomic, retain) NSString *attribute1;
#property (nullable, nonatomic, retain) Note *note;
The thing to realise is that the note relationship is of class Note. It's not a foreign key, or a noteid, that you have to use to lookup the corresponding Note object. It is the Note object. Under the hood, CoreData is adding primary keys and foreign keys to the underlying tables, but all that aggravation is abstracted away.
Having created the subclasses, you can use the dot-accessors for the object properties:
NSString *myStringValue = myItem.attribute;
myItem.attribute = #"new value for attribute";
For the relationship, if you have an Item object called myItem and a Note object called myNote, you can set the relationship value with:
myItem.note = myNote;
or equivalently:
myNote.item = myItem;
(Note: use one or the other, not both; CoreData automatically sets inverse relationships for you).
Now, you have the added complication of a web server from which the Item objects and Note objects are downloaded. And on your web server, your Notes table has a field for the itemid, which is used to link Items and Notes. At some point, you want to link up Note objects and Item objects using the itemid. The usual approach would be to do it once (as soon as the CoreData objects are synchronised from the server), set the relationship accordingly, and thenceforth use the relationship rather than the itemid to get the note for a given item. For example, if you are creating a new Note object, and the itemid from the server is "1234", you might do this:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Item"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"itemid == %#", #"1234"];
NSError *error;
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
// should check for nil results/error
if (results.count > 0) {
// found (at least) one Item with itemid == #"1234"
// use the first to set the relationship
newNote.item = results[0];
}
Then whenever you have a particular Item object, you can access the corresponding note using
Note *myNote = myItem.note;
Furthermore, you can cascade the dot-notation, so get the value of attribute for the Note for myItem, use:
NSString *noteText = myItem.note.attribute;
EDIT
Your save method is very close: either set self.note = record before you save, or use newItem.note = record:
//in save method
// Create Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Notes" inManagedObjectContext:self.managedObjectContext];
// Initialize New Record ie newNote
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
// Populate Record
[record setValue:note forKey:#"note"];
[record setValue:localid forKey:#"localnid"];
// Save Record
NSError *error = nil;
if ([self.managedObjectContext save:&error]) {
// If note saved, save new item…
if (itemlength>1) {
Items *newItem = [NSEntityDescription insertNewObjectForEntityForName:#“Item” inManagedObjectContext:self.managedObjectContext];
newItem.item = item;
newItem.note = record;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Error: %#", [error localizedDescription]);
}
}
}
To reach your goal you need to create a NSFetchRequest passing it the right NSPredicate.
The fetch request will be run against your Note entity. The predicate will allow you to specify that the Note object you want to retrieve is the one for that specific noteid.
So, if you have a 1-to-1 relationship between Item and Note, the NSPredicate should like the following:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"relationshipToItem.propertyForItemId == %#", yourItemId];
[request setPredicate:predicate];
Here I suppose you've a created a relationship between the two entities, otherwise you need to do it manually. Can you provide how your model looks like?

iOS How to save data in Core Data from Text Fields and show these data in tableview?

Hi i am Newbie in iOS and first time working on Core Data my task is that i have three text fields [name, email, dob] an one save button when i put data i text fields and press save button then data from text field save in core data and move to next page to show data in tableview.
I have build core data file named as CoreData.xcdatamodeld and one entity and three attributes name, email and date of birth.
-(IBAction)saveButton:(id)sender{
if (switchButton.isOn)
{
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *transaction = [NSEntityDescription insertNewObjectForEntityForName:#"Shruti" inManagedObjectContext:context];
NSManagedObject *name = [NSEntityDescription insertNewObjectForEntityForName:#"name" inManagedObjectContext:context];
NSManagedObject *email = [NSEntityDescription insertNewObjectForEntityForName:#"email" inManagedObjectContext:context];
NSManagedObject *dob = [NSEntityDescription insertNewObjectForEntityForName:#"dob" inManagedObjectContext:context];
[name setValue:self.nameTextField.text forKey:#"name"];
[email setValue:self.emailTextField.text forKey:#"email"];
[dob setValue:self.dobTextField.text forKey:#"dob"];
NSLog(#"Switch is On and data is saved in Core Data");
TableViewController *objOfView = [[TableViewController alloc]initWithNibName:#"TableViewController" bundle:nil];
[self.navigationController pushViewController:objOfView animated:YES];
}
else{
NSLog(#"Switch is Off and data not saved in Core Data");
}
}
In core data i have entity name as Shruti nad three attributes name as name email and dob.
Is it right if not then where i am wrong?
If you've set up your Entity and Attributes correctly then you should be able to add with something like this.
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *transaction = [NSEntityDescription insertNewObjectForEntityForName:#"Shruti" inManagedObjectContext:context];
[transaction setValue:self.nameTextField.text forKey:#"name"];
[transaction setValue:self.emailTextField.text forKey:#"email"];
[transaction setValue:self.dobTextField.text forKey:#"dob"];
// Save the context
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Save Failed! %# %#", error, [error localizedDescription]);
}
But it looks like you've created four different entities, rather than one entity with three attribute.
Go back to your data model file and make sure your Shruti has it's three attribute. If not simply create one by pressing "+" button, set it's name, and set it's attribute's type as "String".
And make sure to delete those Entities name, email, and dob. Hope this will help.

CoreData: Create temporary models and maybe save to context

I have a problem with Core Data, because I don't know the best way to handle my problem:
I load a json from a server and parse the results in ManagedObjects. At first the ManagedObjects should be temporary.
But the user can put a ManagedObject to a leaflet. And then the ManagedObject should be saved to CoreData. (The object should be accessible offline)
When the user load the same objects some time later from the server, already saved ManagedObjects should be fetched.
So I don't want to put every object in CoreData / PersistantStore the user doesn't need.
First what I do is to create a background context:
__block NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.parentContext = context;
With a fetch I check if there is already a ManagedObject in the persistantstore.
If there is one, I will take this. else create a new ManagedObject in nil context.
NSArray *results = [backgroundContext executeFetchRequest:fetch error:&error];
if (!error && results.count == 1) {
myModel = [results objectAtIndex:0];
}
else {
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyModel" inManagedObjectContext:backgroundContext];
myModel = (MyModel *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
}
And I do the same with every relationship:
if (! myModel.relation) {
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Relation" inManagedObjectContext:backgroundContext];
myModel.relation = (Relation *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:myModel.managedObjectContext];
}
Works fine so far with creating the models.
But how to save one model?
The managedObjectContext is nil. If I call save: on the managedObjectContext, it saves everything.
In my AppDelegate i wrote a function to insert a ManagedObject in the main ManagedObjectContext:
- (void)insertObjectAndSave:(NSManagedObject *)managedObject {
if (!managedObject.managedObjectContext) {
[self.managedObjectContext insertObject:managedObject];
}
[self saveContext];
}
Is this a good solution? or is there a better way to save temporary ManagedObjects in the main ManagedObjectContext?
Excellent Answered My Mundi..
Here is on more scenario to create NSManagedObject temporary, whether we can make it permanents If we want.
Create temporary object
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
Code this if you wants to save it permanently
[managedObjectContext insertObject:unassociatedObject];
NSError *error = nil;
if (![managedObjectContext save:&error])
{
//Respond to the error
}
You could not create the objects with nil context but with a "temp" context. If you want to save them, call [tempContext save:nil];. If you want to discard them, just throw away the context.
Another strategy is to avoid the complexity of multiple context altogether by adding a simple boolean attribute temp to your entity (if relationships are one-to-many, only the top level entity needs to have this). You can save by default and display only non-temp items. You can delete temp items, including all related items, immediately or periodically.

Core-data many-to-many relationship

I have an Android app, and now I'm making an iOS version, but I'm having some problem with the joins in CoreData.
I have the following tables:
Cidade
-cid_codigo integer primary key
-cid_nome text
-cid_nome_normalizado text
Anunciante
-anu_codigo integer primary key
-anu_nome text
-some other values
AnuncianteCidade
-cid_codigo integer
-anu_codigo integer
To get the all data from table Cidade I use the following method:
+(NSMutableArray *)getAllCidades{
NSMutableArray *retorno = [[NSMutableArray alloc] init];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Cidade" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
//WHERE CLAUSE
NSPredicate *pred = [NSPredicate predicateWithFormat:#"1 = 1"];
[request setPredicate:pred];
NSError *error;
NSArray *cidades = [context executeFetchRequest:request error:&error];
if([cidades count] == 0){
NSLog(#"Nenhuma cidade encontrada");
}else{
for(NSManagedObject *cidade in cidades){
Cidade *c = [[Cidade alloc] init];
[c initWithCodigo:[[cidade valueForKey:#"cid_codigo"] integerValue] nome:[cidade valueForKey:#"cid_nome"] nomeNormalizado:[cidade valueForKey:#"cid_nome_normalizado"]];
[retorno addObject:c];
}
}
return retorno;
}
But now, given a name from Cidade, I want to get all the data from Anunciante associated with this Cidade.
How can I do that?
Core Data is not a database. Core Data is an object graph that happens to persist to disk and one of the formats that Core Data can persist to is a database structure. This is an important distinction that will help you to work with it moving forward.
First, you cannot call just -init on a NSManagedObject. That will not work as it is not the designated initializer of NSManagedObject. I would suggest you read up on Core Data and learn how to stand up the Core Data stack.
Having said that, your Cidade objects should have a reference to Anunciante. The join table is internal to Core Data and you don't have access to it nor should you. To get all of the Anunciante objects for a Cidade object is to simply request the objects:
Given an NSArray of Cidade objects:
NSArray *objects = ...;
for (NSManagedObject *object in objects) {
NSSet *anunciantes = [object valueForKey:#"anunciante"];
}
This is assuming you have a many to many relationship defined in the Core Data model editor between the Cidade and the Anunciante entities.
In addition to Marcus' answer, I would add that a predicate "1 = 1" could be simply left out.
To insert a managed object into the context you use a NSEntityDescription class method:
Cidade *cidade = [NSEntityDescription insertNewObjectForEntityForName:#"Cidade"
inManagedObjectContext:context];
All "anunciantes" of a cidade will be conveniently available to you as a NSSet:
cidade.anunciantes
is all you need.

How to insert several NSManageObjects in a one to many relationship?

I have from one side the NSManagedObject event that has a relationship one to many with the Entity Contacts.My app downloads contacts from the server, once all the contacts are downloaded I want to save them in CoreData keeping the relationship. Im wondering if I can save them in one go. All the contacts in an NSArray, contactsWeb, and push them into Core Data or I have to save each one of them like this:
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
for(int i = 0; [contactsWeb count]; i++){
NSManagedObject *contact = [NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
[contact setValue:[contactsWeb objectAtIndex:i] forKey:#"text"];
[contact setValue:[NSDate date] forKey:#"date"];
[[event mutableSetValueForKey:#"toContacts"]addObject:contact];
NSError *error;
if(![context save:&error]){
NSLog(#"%#", [error localizedDescription]);
}
}
Saving a managed object context means that all changes in the context are saved to the persistent store (or to the parent context, in the case of nested contexts). So you can (and should) save the context "in one go" after all contacts have been inserted and the relationships been set.
Remark: If you create managed object subclasses Contacts and Event for your entities, your code can be simplified to
Contacts *contact = [NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
contact.text = [contactsWeb objectAtIndex:i];
contact.date = [NSDate date];
[event addToContactsObject:contact];
The managed object subclasses can be created in Xcode: Select the entities in the Core Data model editor and choose "Editor -> Create NSManagedObject subclass ..." from the menu.

Resources