Core Data Relationships fetch - iphone

Ok, I thought I had this but I am not getting the results that I am expecting. Hopefully someone can help.
I have two entities Person and Timesheet with one attribute to-many relationship:
Person.timesheet<--->>Timesheet.user.
The code below works but when I try to add a second timesheet entry it seems to override the first?
I have looked at the Apple Docs and they are a little vague on this subject.
//Add
NSManagedObjectContext *context = self.managedObjectContext;
Person *personAdded = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
Timesheet *timesheet = [NSEntityDescription insertNewObjectForEntityForName:#"Timesheet" inManagedObjectContext:context];;
timesheet.time = #"10:00 Friday";
timesheet.timestamp = [NSDate date];
NSSet *timesheetSet = [NSSet setWithObject:timesheet];
personAdded.name = #"Darren";
personAdded.job = #"Job to be Done";
personAdded.timesheet = timesheetSet;
NSError *error = nil;
[context save:&error];
if (error) {
NSLog(#"[ERROR] COREDATA: Save raised an error - '%#'", [error description]);
}
NSLog(#"[SUCCESS] COREDATA: Inserted new User to database!");
// Load
NSEntityDescription *personEntity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity: personEntity];
error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (!results || error) {
NSLog(#"[ERROR] COREDATA: Fetch request raised an error - '%#'", [error description]);
[request release];
}
NSLog(#"Results: %#",results);
Person *firstUser = [results objectAtIndex:0];
NSLog(#"First User's name: %#",firstUser.name);
NSLog(#"First User's time %#",[[firstUser.timesheet anyObject] valueForKeyPath:#"timestamp"]);
I am wondering if it could be because I am actually setting the Person.timesheet key with the NSSet and not the actual Table? OR could it be that I am not calling the results correctly?
Thanks,
Darren

You should have a generated "CoreDataGeneratedAccessors" method in your Person.h file which gives you a method
[personAdded addTimesheet:timesheetSet];
which creates the links for you ?
Core Data normally generates an add and a remove method for any relationships you define.
Post your person.h file if this is not clear.

Related

Core data:- How to update values in the array (Transformable)

I work on app that use Core data to save data in local device. In Core data i have save data in array using Transformable format but, i don't know how to update particular values in the array.
My code for update Array is here
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *user = [NSEntityDescription insertNewObjectForEntityForName:#"Type" inManagedObjectContext:context];
NSError *error = nil;
//Set up to get the thing you want to update
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"businessTypes == %#", #"Others"];
[request setPredicate:predicate];
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
} else {
// At least one object saved. There should be only one
// so use the first...
user = [results lastObject];
[user setValue:#"Management" forKey:#"businessTypes"];
}
}
if (![self.managedObjectContext save:&error]) {
//Handle any error with the saving of the context
}
else{
[app saveContext];
NSLog(#"update value successfully");
}
and below is my save array in core data:
{
businessTypes = (
"Social Bussiness",
Marketing,
Transports,
Others
);
},
so i want to update "Others" to "Management" in the array.
When i run this code i have no error but i don't update particular value at index array.
thanks to help me.
Perhaps you are confusing your entities. You fetch an entity called Type but you are calling the object user, indicating that perhaps you wanted to fetch a user that has a certain business type.
If each user has only one "business type", you do not need a Type entity, just a string attribute for the User entity.
If each user can have more than one business type, you should have an entity Type with a name attribute that includes one term indicating the business type, and it should be modeled as a many-to-many relationship.
User <<--------->> Type
To set all types that are now called "Other" to "Management", you would fetch the Type with name "Other", change it and save. To only change one of a user's business types from "Other" to "Management", you would: fetch the user, remove the "Other" type, fetch the "Management" type, add it to the user and save.
If your businessTypes attribute is supposed to be a transformable array with hard-coded strings, you should probably change the data model as described above. You will have much more flexibility and power for searching and handling the data with a clean Core Data model.
You have to modify your update function like this code then you will get your required output
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"inManagedObjectContext:context];
[request setEntity:entity];
request.propertiesToFetch= #[ #"businessTypes"];
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
} else {
int loopCount = (int)results.count;
Type* entityType = nil;
for (int index=0; index<loopCount; index++) {
entityType = (Type*)results[index];
if (entityType.businessTypes!=nil) {
NSUInteger reqIndex = [entityType.businessTypes indexOfObject:#"Others"];
[entityType.businessTypes replaceObjectAtIndex:reqIndex withObject:#"Management"];
[entityType setValue:entityType.businessTypes forKey:#"businessTypes"];
}
}
}
if (![self.managedObjectContext save:&error]) {
//Handle any error with the saving of the context
}
else{
[app saveContext];
NSLog(#"update value successfully");
}

Why is executeFetchRequest:fetchRequest leaking memory?

Instruments shows the following code leaks, if I comment out this code there is no leak.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:USER_CORE_DATA inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicte = [NSPredicate predicateWithFormat:#"username == %#", [[User defaultManager] savedUsername]];
[fetchRequest setPredicate:predicte];
// set any predicates or sort descriptors, etc.
// execute the request
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) {
} onFailure:^(NSError *error) {
NSLog(#"Error fetching: %#", error);
}];
[fetchRequest release];
Specifically instruments says this line in the code above:
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results)
It appears to be a leak with fetchRequest and/or the block. Any help will be appreciated, and thanks in advance.
It appears executeFetchRequest:onSuccess:onFailure: is a function you have defined in NSManagedObjectContext category. Ensure the NSArray object instance that you are passing to the onSuccess block is autoreleased.
Actually it turned out that StackMob had a leak in their code, I downloaded there source and fixed it.
- (NSString *)primaryKeyField
{
NSString *objectIdField = nil;
// Search for schemanameId
objectIdField = [[self SMSchema] stringByAppendingFormat:#"Id"];
if ([[[self entity] propertiesByName] objectForKey:objectIdField] != nil) {
return objectIdField;
}
objectIdField = nil; // This line was missing and causing a leak
// Search for schemaname_id
objectIdField = [[self SMSchema] stringByAppendingFormat:#"_id"];
if ([[[self entity] propertiesByName] objectForKey:objectIdField] != nil) {
return objectIdField;
}
objectIdField = nil; // This line was missing and causing a leak
// Raise an exception and return nil
[NSException raise:SMExceptionIncompatibleObject format:#"No Attribute found for `entity %# which maps to the primary key on StackMob. The Attribute name should match one of the following formats: lowercasedEntityNameId or lowercasedEntityName_id. If the managed object subclass for %# inherits from SMUserManagedObject, meaning it is intended to define user objects, you may return either of the above formats or whatever lowercase string with optional underscores matches the primary key field on StackMob.", [[self entity] name], [[self entity] name]];`

Saved string in CoreData returns <null> and the string. Why?

Hi i am saving values coming from a database in a MultableArray and then in CoreData:
NSMultableArray *mutary = [[NSMultableArray alloc] init];
NSManagedObjectContext *context = [app managedObjectContext];
for(int n=0; n<[AttributeArray count]; n++)
{
[mutary addObject:[[AttributeArray objectAtIndex:n] objectForKey:#"AttributName"]];
NSLog(#"%#", mutary);
}
attributeString = [mutary componentsJoinedByString:#","];
raume = [NSEntityDescription insertNewObjectForEntityForName:#"Raum" inManagedObjectContext:context];
raume.raumattribut = attributeString;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog output for the MultableArray is:
2012-06-20 17:21:00.047 book-app[31984:15803] (
A7OVERHEAD,
Beamer
)
So far its working correct. The two expected values from the database are now in the Array.
Now i am fetching these attributes from CoreData:
NSManagedObjectContext *context = [app managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Raum" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error = nil;
NSArray *events = [context executeFetchRequest:request error:&error];
for (Raum *att in events)
{
stringAttribute = [[events valueForKey:#"raumattribut"] componentsJoinedByString:#","];
NSLog(#"ATTRIBUTE: %#", stringAttribute);
}
}
So far so good. But if i look now on my NSLog output:
2012-06-20 17:21:00.055 book-app[31984:15803] ATTRIBUTE: <null>,A7OVERHEAD,Beamer
CoreData is returning and then the two values. Where is that coming from?
Can someone help?
Thanks in advance
EDIT: After some investigating and clarifying (see comments), here is my answer:
Calling valueForKey on an NSArray returns an array where each element is created by calling valueForKey on each member of the original array. This means your output indicates that you have 3 total objects returned by your fetch request, and the first one does not have it's raumattribut attribute set.
PREVIOUS ANSWER:
You are calling valueForKey on an instance of NSArray (the query results). Perhaps you need to get objectAtIndex:0 first use the variable you are iterating the values on? valueForKey is likely returning nil.
stringAttribute = [[att valueForKey:#"raumattribut"] componentsJoinedByString:#","];
or similar.
EDIT:
At first I missed the for loop in the second part, I have edited the above code sample to use the attr, which

1 to 1 relationship core data iOS

I have two entities: Login (user id, password) and Information (title, info).
Now there is a 1 to 1 relation between them.
I need to add some information in the database unique to a user.
My code is below here:
Login *information = [NSEntityDescription insertNewObjectForEntityForName:#"Login"
inManagedObjectContext:self.managedObjectContext];
information.information.title = informationTitleTextView.text;
information.information.info_1 = information1textview.text;
information.information.info_2 = information2textview.text;
[self.managedObjectContext save:nil]; // write to database
[self.delegate savebuttontapped:self];
But It's not Working.I don't Know, What i am doing wrong?Any help would be appreciated.
You haven't added an instance of Information to the context. Try this:
Login *login = [NSEntityDescription insertNewObjectForEntityForName:#"Login" inManagedObjectContext:self.managedObjectContext];
Information *information = [NSEntityDescription insertNewObjectForEntityForName:#"Information" inManagedObjectContext:self.managedObjectContext];
login.information = information;
login.information.title = informationTitleTextView.text;
//...and so on...
Of course, if you're going to fetch a Login object based on its attributes, you'll want to actually store something in those attributes:
login.userId = theUserId;
login.password = thePassword;
At some point in the future, you'll probably want to fetch just the Login object that matches your criteria. Once you have that, you can get the associated information object without any trouble:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Login"
inManagedObjectContext:managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userId like %# AND password like %#", theUserId, thePassword];
[request setPredicate:predicate];
NSError *err = nil;
NSArray *matchingLogins = [self.managedObjectContext executeFetchRequest:request error:&err];
int count = [matchingLogins count];
if (count != 1) {
NSLog(#"Houston, we have a problem.");
}
Login *login = [matchingLogins objectAtIndex:0];
Information *info = login.information; // Notice: no separate fetch needed

Auto-Incremented Object ID in Core Data?

I am working with several NSManagedObject types with several relationships. How can I tell Core Data to automatically populate object IDs for me? I'm looking for something like an index key in SQL, so that no two instances of a given object are allowed to have the same ID.
Edit:
I'd like for all of my "Account" objects to have unique IDs on them. I was just adding one to the `countForFetchRequest, but I realized that when deleting the second to last object and then adding one, the last two objects now have the same IDs.
How can I ensure that a given value has a unique value for all instances of my "Account" NSManagedObject?
EDIT2:
I need to have a separate ID for sorting purposes.
All NSManagedObjects automatically have a unique NSManagedObjectID. There is no notion of a custom auto-incrementing attribute, but it's certainly easy to write one yourself.
The way I resolved this is with Core Data aggregates. I actually end up assigning the ID myself.
Essentially, I query Core Data for all of the entity IDs of my entity and then iterate through them. If I find an ID which is higher than the current temporary one, I make the temporary ID higher one higher than the aggregated one. When I'm done, I automatically have an ID which is higher than the highest one in the list. The only flaw I see with this is if there is a missing ID. (I believe that there is a simple fix for this as well.)
//
// Create a new entity description
//
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:self.managedObjectContext];
//
// Set the fetch request
//
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:entity];
//
// We need to figure out how many
// existing groups there are so that
// we can set the proper ID.
//
// To do so, we use an aggregated request.
//
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:#"entityID"]];
NSError *error = nil;
NSArray *existingIDs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error != nil) {
//
// TODO: Handle error.
//
NSLog(#"Error: %#", [error localizedDescription]);
}
NSInteger newID = 0;
for (NSDictionary *dict in existingIDs) {
NSInteger IDToCompare = [[dict valueForKey:#"entityID"] integerValue];
if (IDToCompare >= newID) {
newID = IDToCompare + 1;
}
}
//
// Create the actual entity
//
MyEntity *newEntity = [[MyEntity alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
//
// Set the ID of the new entity
//
[newEntity setEntityID:[NSNumber numberWithInteger:newID]];
//
// ... More Code ...
//
Accroding to your EDIT2 and Edit3, following answer will help you.. Assume your id field as NSNumber having unsignedInt as ID.
1) Fetch all records for corresponding entity.
NSError *error = nil;
NSArray *array = [self fetchAllFileEntity:&error];
2) Find maximum number belonging to that result.
NSNumber *maxValue = nil;
if (array)
maxValue = [array valueForKeyPath:#"#max.uniqueId.unsignedIntegerValue"];
else
maxValue = [NSNumber numberWithUnsignedInteger:0];
3) Assign maxValue+1 to your new entity
entity.uniqueId = [NSNumber numberWithUnsignedInteger:maxValue.unsignedIntegerValue+1];
I have come up with this solution for the said problem, hope it's gonna be helpful for some one.
AppDelegate *appdelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appdelegate managedObjectContext];
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *chatHist = [NSEntityDescription
entityForName:#"ChatHistory" inManagedObjectContext:context];
[fetchRequest setEntity:chatHist];
int chatIdNumber = 0;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if ([fetchedObjects count] > 0) {
ChatHistory *chatHistObj = [fetchedObjects objectAtIndex:[fetchedObjects count]-1];
chatIdNumber = [chatHistObj.chatId intValue];
}
chatIdNumber = chatIdNumber+1;
ChatHistory *chat_History = [NSEntityDescription insertNewObjectForEntityForName:#"ChatHistory" inManagedObjectContext:context];
chat_History.chatId = [NSString stringWithFormat:#"%d",chatIdNumber];