How to get object reference in Core Data? - ios

There is an entities where I am filling data from JSON, Say it is Photographer and Photo. Both have some data which I have filled using loop and managed ObjectContex ..
Like this,
NSMutableArray *ArrPhotographer= [[self.M3Arr objectForKey:#"LifeMag"]objectForKey:#"Photographer"];
for (int i = 0; i< ArrPhotographer.count; i++) {
Photographer * photographerObj = [NSEntityDescription insertNewObjectForEntityForName:#"PhotographerData"
inManagedObjectContext:[self managedObjectContext]];
NSMutableDictionary *tpDict = [cleanerListArr objectAtIndex:i];
photographerObj.cleanerName = [tpDict objectForKey:#"photographerName"];
}
Now I have done this for both Photographer and Photo Entity and as per this picture my Magazine entity is having data which are already existed in this table . And as shown I have made to one relation to both Photo and Photographer from Magazine .
Now Question is ,
If Photographer Name is already existed in the table , How can I connect it with Magazine Entity . I need the managed object Reference of that particular place .
(For Example , There are three photographer , Ron , Harry and Sunny now For Photo Cover1 I want name of Ron . then I need the Object Reference of Ron when I pre Populate it).
How to get this object Reference?
// **************** EDIT
I am getting the object is present ...but No how to fetch object and 2) how to give it to x and y stated above ?
I am using this code to saving the data in Magazine
Magazine *magObj = [NSEntityDescription insertNewObjectForEntityForName:#"Magazine"
inManagedObjectContext:[self managedObjectContext]];
magObj .issueID=[NSNumber numberWithInt:1];
magObj .photo= x;
magObj .photographer = y;
#### Edit 2
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Cleaner"];
[request setPredicate:[NSPredicate predicateWithFormat:#"cleanerName = %#", #"Robin"]];
[request setFetchLimit:1];
NSUInteger count = [[self managedObjectContext] countForFetchRequest:request error:nil];
if (count == NSNotFound){
NSLog(#"ERROR FOund");
}
// some error occurred
else if (count == 0){
NSLog(#"no matching object");
}
// no matching object
else{
NSLog(#"Found Match");
}

You need to use an NSFetchRequest to search the context for the appropriate object to connect to. The fetch request specifies the entity type to search for and you need to add an NSPredicate to filter the results to only the particular name that you're interested in.
Note that you could run a single fetch request with a list of names so that you only hit the data store for information once and then use the returned list during your object creation / connection loop.
If you're loading all of your data in one go, then you can create a dictionary which contains the managed object instances such that you can link to them without fetching.

Related

IOS/Objective-C: retrieving ManagedObjectID within loop error

I am having trouble setting and retrieving a managedObjectId within a loop. First problem, I can't find in the docs what the parts of the MOID mean. So first question, are the following moids unique? The only way that they are different is in the last digit after the entity name, Item. If not, that could be the issue.
0xd000000054200000 <x-coredata://10EC1628-A6D4-487B-BF5C-61EAD9838132/Item/p5384>
0xd000000054240000 <x-coredata://10EC1628-A6D4-487B-BF5C-61EAD9838132/Item/p5385>
Second question, if they unique, when I retrieve the record associated with these ids, I end up retrieving the same record. So maybe there is a problem in the loop below.
Here is my code simplified slightly as there is a sync to server that I have not included.
//NSArray * myItems is an array of items to be saved
for (i=0;i<max;i++)
{
currentItem = myItems[i];
// Create Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:self.managedObjectContext];
// Initialize Record
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
// Populate Record
[record setValue:currentName forKey:#"name"];
// Save Record
NSError *error = nil;
if ([self.managedObjectContext save:&error]) {
//Set moID in ivar of saved record
self.moID = [record objectID];
[self syncAndMarkSynced];
}
}//close loop
-(void) syncAndMarkSynced{
//sync to server omitted
Items *object = [self.managedObjectContext objectRegisteredForID:self.moID];
object.synced = #1;
}
First problem, I can't find in the docs what the parts of the MOID mean.
That's because they are not documented. The object ID is unique; the details are not explained because the parts of the URI are not intended to be meaningful on their own.
Second question, if they unique, when I retrieve the record associated with these ids, I end up retrieving the same record.
That's expected. A managed object has a unique ID. When you look up a managed object by ID, you're requesting the same entry from the persistent store. Each entry has a unique ID, so if you use the ID, you get that entry.
So maybe there is a problem in the loop below.
It's not clear to me what the loop is trying to do. Hopefully the information above will help you work it out.

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 / Core Data Entity Relationships with Pre-Defined Data

I have two Objective-C Core Data entities - say Person and Nationality. Person would have a To-One relationship with Nationality, whilst Nationality would have a To-Many relationship with Person. Also, Person class can have any number of objects / rows, whereas Nationality would have a pre-defined list of 200 odd instances. So Person should not be able to assign a nationality to itself other than those 200 objects.
Can someone advise please how would we code this out or in case there is a sample code available? Afraid I dont seem to be able to get a start on how to leverage setValue: forKey: here...
Much appreciated!
Let's assume that your Nationality entity has a "name" attribute that uniquely identifies that nationality. You can provide any manner of UI to get this from the user. It can be typing in a string or fetching all nationalities and putting them in a table or some kind of picker.
If you want all nationalities, that's easy.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Nationality"];
NSError *error;
NSArray *nationalities = [moc executeFetchRequest:fetchRequest error:&error];
if (nationalities == nil) {
// handle error
} else {
// You now have an array of all Nationality entities that can be used
// in some UI element to allow a specific one to be picked
}
If you want to look it up based on a string for the name...
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Nationality"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"name = %#", nationalityName];
fetchRequest.fetchLimit = 1;
NSArray *nationalities = [moc executeFetchRequest:fetchRequest error:&error];
if (nationalities == nil) {
// handle error
} else {
NSManagedObject *nationality = [nationalities firstObject];
// If non-nil, it will be the nationality object you desire
}
Creating the person and assigning its nationality is also straight forward...
if (nationality) {
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:moc];
// Set any attributes of the Person entity
[person setValue:#"Fred Flintstone" forKey:#"name"];
// Assign its nationality, and as long as the relationship is setup with
// inverse relationships, the inverse will be automatically assigned
[person setValue:nationality forKey:#"nationality"];
}

objective c - set relationship (core data)

I'm very new to core data and the project that I'm currently working on involves using core data for local data storage.
Below is the structure of my entities.
My app functions as follows. When the user first launches the app, there are presented with a login screen. New users can register. When a user tries to register, they have to enter a unique key that has already been provided to them, prior to the registration. This unique key is validated in the Facility table. If the key exists, in the db, then I will enter the user data into the database and will create a relationship between the Assessor and the Facilitythat they just entered the code for.
My question is, how would I create a relationship between a new Assessor and an existing Facility?
I'm thinking, I should fetch the required Facility object from core data and then use [assessorsetValue:facilityObjectforKey:#"facility"] to set the object.
Can anyone help me understand how to fetch the facilityObject from the Facility table in core data before assigning it to the Assessor?
Thanks in advance.
EDIT:
For example:
This is how my Facility table will look like.
code | name
H6DJ | Computing
So if the user (John Doe) enters H6DJ as the registration code, I check the Facility table to see if it exists. so in this case, I would like to create a relationship between the new user (John Doe) and the Computing facility.
The Core Data Programming Guide provides lots of sample code.
Your idea about setting the facility property of the new assessor entity is correct - you just need to fetch the facility first - presumably you would need to do this anyway to validate the facility code that was entered.
NSManagedObject *newAssessor = [NSEntityDescription
insertNewObjectForEntityForName:#"Assessor"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"Facility"
inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"code == %#", targetCode];
[request setPredicate:predicate];
NSError *error;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if (array != nil) {
NSUInteger count = array.count;
if (count == 1) {
NSManagedObject *facility=array[0];
if (facility["assessor"]==nil) {
newAssessor["facility"]=facility;
// Set up rest of assessor attributes before saving
} else {
// Error - facility already assigned to assessor ?
}
} else if (count == 0) {
// Code not found/valid - do something
} else {
// More than 1 matching facility - data error
}
}
else {
// Deal with error.
}

Filter CoreData (Parent entity and child entity)

Two days I have been trying to filter the CoreData and I'm still stuck.
This is my model:
Users < --- >> Sessions
I managed to create the relationship between a new session and a specific user. I also managed to get the list of all the users to display them in a tableview by this code :
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *usersDescription= [NSEntityDescription entityForName:#"Users" inManagedObjectContext:_managedObjectContext];
[request setEntity:usersDescription];
NSError *error = nil;
NSMutableArray *mutableFetchresults = [[_managedObjectContext executeFetchRequest:request error:&error]mutableCopy];
if(mutableFetchresults == nil) {
//handle error
}
usernameDataMutableArray = [[NSMutableArray alloc]init];
for (int i = 0; i< [mutableFetchresults count]; i++)
{ Users* users = (Users *) [mutableFetchresults objectAtIndex:i];
[usernameDataMutableArray insertObject: [users username] atIndex:i];
}
Now when I touch a cell called "Username1", I display a new tableView. I would like display in this tableView all the Username1 sessions.
So my question is: how can I filter all my sessions to retrieve these belong to Username1?
There is no filter required.
When you push the new view controller you hand off the instance of Users that was selected. Then in the new view controller you ask the instance of Users for the Sessions instances that are associated with it.
NSSet *sessionsSet = [myUser valueForKey:#"sessions"];
You can also use a property if you have subclasses set up.
This is the point of Core Data. It is an object graph and relationships are properties on the object instance. Just call the appropriate method.
I would strongly suggest reading a book on Core Data as it will clear up a lot of the issues you are having with these fundamental concepts.
Additional Comments
Entities should not be named in the plural. Your entities should be called User and Session instead of their plural form. The name of the relationship that points to a to-many should be plural and the name that points to the to-one should be singular. This helps greatly with code clarity and maintainability. Plural tends to indicate a collection of objects instead of a single object like you have now.

Resources