Core-data many-to-many relationship - ios

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.

Related

core data relationship, how to insert and retrieve data from relationships

I am new in IOS development and I am having a problem when using core data specially when using relationships
I have the following relationship
enter image description here
as you see i have tree entities and what i am trying to do is insert into CredentialsFood entity the data when food is consumed by a particular user
so I am trying to insert using the following code
// adding to credetialsFood Entity
NSManagedObjectContext *context1 = [appD managedObjectContext];
CredentialsFood *credentialsFood = (CredentialsFood *) [NSEntityDescription insertNewObjectForEntityForName:#"CredentialsFood" inManagedObjectContext:context1];
NSDate *currentDate = [NSDate date];
credentialsFood.toFood.food_id = item_id;
credentialsFood.toCred.email = #"123";
credentialsFood.date = currentDate;
// here's where the actual save happens,
if(![context1 save:&errorCoreData]){
NSLog(#"Problem saving: %#",[error localizedDescription]);
}
else{
NSLog(#"Food date Saved");
}
the code works fine
however when retrieving I try for example get all CredentialsFood.date where Credentials.email = 123
I receive nothing
I don't really know What i am doing wrong this is the code to retrieve
NSManagedObjectContext *context2 = [appD managedObjectContext];
NSEntityDescription *foodEntity =[NSEntityDescription entityForName:#"Credentials" inManagedObjectContext:context2];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSError *error2 = nil;
NSString * userid =#"123";
NSPredicate *foodPredicate = [NSPredicate predicateWithFormat:#"(email contains[cd] %#)",userid];
[request setEntity:foodEntity];
[request setPredicate:foodPredicate];
NSManagedObject *matches = nil;
NSArray *objects = [context executeFetchRequest:request error:&error2];
if([objects count]== 0){
NSLog (#"no matches found");
}
else {
//NSLog(#"aasdasdad %#", objects);
matches = [objects objectAtIndex:0];
NSLog(#"bb %#",[matches valueForKey:#"name"]);
NSLog(#"bb %#",[matches valueForKey:#"email"]);
NSLog(#"bb %#",[matches valueForKey:#"password"]);
//NSLog(#"hhhhhhhhhhhhhh %#",[matches valueForKey:#"credentails"]);
NSMutableSet *query = [matches mutableSetValueForKey:#"credentails"];
NSArray *financialData =[matches valueForKeyPath:#"credentails"];
NSLog(#"%#",financialData);
NSLog(#"%#",[financialData valueForKey:#"date"]);
the data is inside core data however i don't know if it is performing the relation, the Food entity is populated and the CredentialsFood is also populated
I also getting the warnings
abstract entity Food has no children
abstract entity Credentials has no children
abstract entity CredentialsFood has no children
Thanks in advance
When you first create the object:
CredentialsFood *credentialsFood = (CredentialsFood *) [NSEntityDescription insertNewObjectForEntityForName:#"CredentialsFood" inManagedObjectContext:context1];
NSDate *currentDate = [NSDate date];
credentialsFood.toFood.food_id = item_id;
credentialsFood.toCred.email = #"123";
...you do not appear to be assigning anything to the toFood or toCred relationships. Related objects are not created automatically, so those properties will be nil. In Objective-C it's not an error to run this code while those properties are nil, but the code has no effect. It looks like you need to create (or fetch) an instance of Food and of Credentials, and assign those new objects to the toFood and toCred attributes.

Exclude duplicate values for NSMutableArray from Coredata entities

I have a method that returns an NSMutableArray of entities from my CoreData database. The entities look like this
#property (nonatomic, retain) NSNumber * iD;
#property (nonatomic, retain) NSString * number;
#property (nonatomic, retain) ManufacturerNumber *manufacturerNumber;
I need to create a unique array of manufacturerNumber entities based of the number NSString.
this is how my method that returns the current array with duplicates looks.
- (NSMutableArray *)readNumber
{
NSManagedObjectContext *context = [self managedObjectContext];
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ManufacturerNumber" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *manufacturerNumbersDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ManufacturerNumber *manufacturerNumber in fetchedObjects) {
[manufacturerNumbersDictionaryArray addObject:manufacturerNumber];
}
return manufacturerNumbersDictionaryArray;
}
This returns an NSMutableArray including duplicates, and I'd like to remove the duplicates.
Update to question
I have now decided to edit the array when I go to display the values in my UITableview, below I explain what the array contains etc.
I have a NSMutableArray that contains the coredata entities described above, I would like to know how to create a NSSet of unique values from the NSMutablerArray based from the entities cNumber attribute.
This is how I have created the NSMutableArray
tableViewMArray = [NSMutableArray arrayWithArray:[cardManufacturerNumber.cNumbers allObjects]];
As you can see cardManufacturerNumber is a coredata object with a one to many relationship with cNumbers.
cNumbers has 3 attributes
numString
numID
parentObj
I would like to know how to create a unique NSMutableArray based off cNumbers numString attribute.
The NSMutableArray should consisit of unique cNumbers coredata objects. not just numString strings as I need to know the other values.
Have you tried using NSMutableSet instead of NSMutableArray? If you properly implement - (NSUInteger)hash and - (BOOL)isEqual:(id)anObject in a way that makes logical sense on your ManufacturerNumber class this should give you the result you want (this is necessary since sets use these methods to ensure the uniqueness of each object. How you implement these depends on the internals of your ManufacturerNumber class). Or, if you care about order but want to maintain uniqueness, you could use NSMutableOrderedSet.
Unless I am missing the point, this should help for the first part of your question...
Regarding your method (NSMutableArray *)readNumber, try this instead...
- (NSSet *)readNumber
{
NSSet *setReturned = nil;
NSManagedObjectContext *context = [self managedObjectContext];
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ManufacturerNumber" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *manufacturerNumbersDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ManufacturerNumber *manufacturerNumber in fetchedObjects) {
[manufacturerNumbersDictionaryArray addObject:manufacturerNumber];
}
setReturned = [NSSet setWithArray: manufacturerNumbersDictionaryArray];
return setReturned;
}
This should return a discrete set of manufacturerNumber without duplicates.
For the second and updated part of your question, I'm struggling to understand exactly what you need, so forgive me if I am off the mark.
Have you attempted an NSFetchRequest with an NSPredicate, instead of the setting the variable tableViewMArray?
For example...
NSString *key = #"cNumbers";
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"cNumber"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K != %d", key, cardManufacturerNumber.cNumbers];
[fetchRequest setPredicate:predicate];
NSArray *array = [<<insert local managedObjectContext>> executeFetchRequest:fetchRequest error:nil];
NSSet *set = [NSSet setWithArray:array];
and then to make the array mutable...
(with thanks to this SO Q&A)
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:[set allObjects]];

fetch coredata relationship

I would like to know how to fetch the items from my coredata relation ship. I guess it should be a dictionary or arrays or something that gets returned so that you can have your one to many thing.
I am quite lost at this I know how to write/read single objects but this relationship stuff is abit confusing.
I think I have sucsessfully written a relationship to coredata however now I would like to be able to read it to see if I have it right.. I have started writing the method for this but have no idea what to actually do to get all of the information out.
this is the code i have so far.. for both read and write
- (void)writeFin:(NSArray *)recivedProjectData ItemsData:(NSArray *)itemsData {
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *dict in recivedProjectData) {
project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:context];
project.proNumber = [dict valueForKey:#"ProNumber"];
project.projectDescription = [dict valueForKey:#"Description"];
// project.items = [dict valueForKey:#""]; // this is the relationship for project
}
for (NSDictionary *dict in itemsData) {
items = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:context];
items.Number = [dict valueForKey:#"Number"];
items.Description = [dict valueForKey:#"Description"];
items.comment = [dict valueForKey:#"Comment"];
items.project = project; // this is the relationship for items
[project addItemsObject:items];
}
NSError *error = nil;
if (![__managedObjectContext save:&error]) {
NSLog(#"There was an error! %#", error);
}
else {
NSLog(#"created");
}
}
- (NSMutableArray *)readFin {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *projectDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ProjectList *projectList in fetchedObjects) {
NSMutableDictionary *tempProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempProjectDictionaryArray setObject:project.proNumber forKey:#"ProNumber"];
[tempProjectDictionaryArray setObject:project.description forKey:#"Description"];
[tempProjectDictionaryArray setObject:project.projectID forKey:#"ProjectID"];
[projectDictionaryArray addObject:tempProjectDictionaryArray];
}
return projectDictionaryArray;
}
So just o reiterate, I would like to know A, is my write method look okay? B, how do you fetch/read the relationship objects from core data.
any help would be greatly appreciated.
A relationship in Core Data isn't an object, its a property which happens to correspond to another object in your model rather than just being a dead end. You're already most of the way there as far as checking whether your relationships are ok as far as I can see -- what you need to do is add one more line in your projectList
[tempProjectDictionaryArray setObject: project.items forKey:#"items"];
the object that you will have added will be an NSSet. You can then check that things are as they should be with a loop like this after you've finished setting things up
NSSet itemsForProject = projectDictionaryArray[someIndex][#"items"]
for (Item* currItem in [itemsForProject allObjects]) {
//access some property of the current item to make sure you have the right ones -- \
description for example
NSLog(#"%#", item.description);
}

ios NSFetchRequest for two related tables

my program has a sqlite database with two related tables. One called "Rank" and other one called "Requirement"
I want to fetch all rows from the "Requirement" table that has a relationship with the specific row in a "Rank" table. Following is my code, it grabs the whole table, but I get the specified rows only according to the above mentioned rule.
-(NSArray *) getAllRequirementsForTheRank:(Rank *) rank
{
NSError *error;
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init]autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Requirement" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate *searchType = [NSPredicate predicateWithFormat:#"Rank = %#", rank];
[fetchRequest setPredicate:searchType];
NSArray *scoutRequirementArray = [self.context executeFetchRequest:fetchRequest error:&error];
for (Requirement *r in scoutRequirementArray)
{
NSLog(#"Requirementttt : %# :", r.requirementName);
}
return scoutRequirementArray;
}
If you have the relationship modelled in core data, just get the linked objects from the relationship property. You don't need another fetch request. rank.requirements will give you an NSSet of everything you need. (I'm assuming names for your object and properties here).

Core Data how to retrieve a column from an entity

I am using Core Data for my ios app and I am wondering how would I go about in retrieving an entire column from an entity table? For example I am soly interested in grabbing the primary key from my table.
In sql i would just do Select name from MYTABLE.
I think you could do it this way :
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"--table--" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
NSMutableArray *elementsFromColumn = [[NSMutableArray alloc] init];
for (NSManagedObject *fetchedObject in fetchedObjects) {
[elementsFromColumn addObject:[fetchedObject valueForKey:#"--column--"]];
}
So you have all the elements from a specific column of your table.
Hope it's what you're looking for :)
Look at the documentation for NSFetchRequest. You can ask it to return dictionaries containing specific properties only - this is about as close as you will get. The methods of interest are setResultType: and setPropertiesToFetch:.

Resources