I have an entity in my core data model like this:
#interface Selection : NSManagedObject
#property (nonatomic, retain) NSString * book_id;
#property (nonatomic, retain) NSString * contenu;
#property (nonatomic, retain) NSNumber * page_id;
#property (nonatomic, retain) NSNumber * nbrOfOccurences;
#property (nonatomic, retain) NSString * next;
#property (nonatomic, retain) NSString * previous;
I have created many Selections and saved them in Core Data and now I would like to delete some selections with some criteria. For example, I would like to delete a Selection object if matches the following:
content = test
page_id = 5
book_id = 1331313
How I can do this?
What Mike Weller wrote is right. I'll expand the answer a little bit.
First you need to create a NSFetchRequest like the following:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Selection" inManagedObjectContext:context]];
Then you have to set the predicate for that request like the following:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"content == %# AND page_id == %# AND book_id == %#", contentVal, pageVal, bookVal]];
where
NSString* contentVal = #"test";
NSNumber* pageVal = [NSNumber numberWithInt:5];
NSString* bookVal = #"1331313";
I'm using %# since I'm supposing you are using objects and not scalar values.
Now you perform a fetch in the context with the previous request:
NSError* error = nil;
NSArray* results = [context executeFetchRequest:fetchRequest error:&error];
results contains all the managed objects that match that predicate.
Finally you could grab the objects and call a deletion on them.
[context deleteObject:currentObj];
Once done you need to save the context as per the documentation.
Just as a new object is not saved to the store until the context is saved, a deleted object is not removed from the store until the context is saved.
Hence
NSError* error = nil;
[context save:&error];
Note that save method returns a bool value. So you can use an approach like the following or display an alert to the user. Source NSManagedObjectContext save error.
NSError *error = nil;
if ([context save:&error] == NO) {
NSAssert(NO, #"Save should not fail\n%#", [error localizedDescription]);
abort();
}
You should perform a fetch request using an NSPredicate with the appropriate conditions, and then call the deleteObject: method on NSManagedObjectContext with each object in the result set.
In addition to Mike Weller and flexaddicted, after calling [context deleteObject:currentObj]; you need to save: context:
NSError *error = nil;
[context save:&error];
As from documentation:
Just as a new object is not saved to the store until the context is saved, a deleted object is not removed from the store until the context is saved.
That made matter in my case.
Related
I need to create a predicate to get object from core data,
My entity "Form" looks like that:
#interface EEForm (CoreDataProperties)
#property (nullable, nonatomic, retain) NSDate *date;
#property (nullable, nonatomic, retain) NSString *descriptor;
#property (nullable, nonatomic, retain) NSString *location;
#property (nullable, nonatomic, retain) NSMutableSet<Participants *> *participants;
entity "Participants" look like that:
#interface Participants (CoreDataProperties)
#property (nonatomic, assign) NSInteger indexRow;
#property (nullable, nonatomic, retain) NSString *participant;
#property (nullable, nonatomic, retain) EEForm *form;
I want to get objects on the base of participants field, that contains all objects from a given array (this array consist of string objects and it changes dependently of what the user selects).
Fetch request is performed by FetchResultController. I set the predicate in initialisation of it.
I did it in that way, but it includes all objects that contains at least one object from the given array.
- (NSFetchedResultsController *)fetchResultController {
if(_fetchResultController != nil) {
return _fetchResultController;
}
NSPredicate *lPredicate = [NSPredicate predicateWithFormat:#"ANY participants.participant IN %#", _selectedForPredicatel];
NSFetchRequest *lRequest = [[NSFetchRequest alloc]init];
[lRequest setPredicate:lPredicate];
NSEntityDescription *lEntity = [NSEntityDescription entityForName:#"Form" inManagedObjectContext:self.managedObjectContext];
[lRequest setEntity:lEntity];
[lRequest setFetchBatchSize:10];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"date" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[lRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *fetchResultController = [[NSFetchedResultsController alloc]initWithFetchRequest:lRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
fetchResultController.delegate = self;
self.fetchResultController = fetchResultController;
NSError *error = nil;
if(![[self fetchResultController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchResultController;
}
Could you please advice how to set predicate properly to filter objects that contain all elements from the given array.
EDIT:
array that is used for predicate looks like that:
_selectedForPredicatel = [[NSMutableArray alloc]initWithObjects:#"Child",#"Adult", #"Group of people", #"Family", #"Teenager", nil];
every time user selects parameters by which filter 'Form' objects, this array is updating, so I need fetch request to be done according to it. So if 'Form' has included participants: #"Child",#"Adult", but in an array _selectedForPredicatel there are objects: #"Adult", #"Group of people", in this case this 'Form' shouldn't be fetched. Should be fetched only that 'Form'that includes both elements of _selectedForPredicatel array.
Thanks a lot for any advice.
You want to include the forms if, for each of the user's selections, there is at least one Participant that has a participant attribute that matches.
If the participant names were unique (at least for each Form), you could count the matches and compare to the count of the user's selections. But if not, you need to create a predicate test each of the user's selections separately, and then combine the results into one big predicate:
NSPredicate *template = [NSPredicate predicateWithFormat:#"(SUBQUERY(participants, $p, $p.participant == $SELECTED).#count > 0)"];
NSMutableArray *predicateArray = [NSMutableArray array];
for (NSString *testString in _selectedForPredicatel) {
NSDictionary *subsVars = #{#"SELECTED" : testString};
NSPredicate *testPredicate = [template predicateWithSubstitutionVariables:subsVars];
[predicateArray addObject:testPredicate];
}
NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicateArray];
NSLog(#"finalPredicate is %#", finalPredicate);
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"ListEntity"];
fetch.predicate = finalPredicate;
Here template is a dummy predicate using $SELECTED as a placeholder for the user's selection. The for loop uses this to create a predicate for each of the user's selections and adds it to a mutable array. The final predicate is built by compounding the array of predicates using AND clauses.
It's pretty messy (the final predicate is bad enough, the underlying SQL is horrible) so performance might be dismal.
Instead of ANY you can use ALL :
NSPredicate *lPredicate = [NSPredicate predicateWithFormat:#"ALL participants.participant IN %#", _sortByParticipants];
This will check if all objects are in your collection.
I am getting the response from JSON is stored in an array and I want to store the values
{albumId albumName coverPhotoURL createdDate} in Core Data please help me.
(
{
albumId = 1;
albumName = UAE;
coverPhotoURL = "http://1-dot-digiphoto-01.appspot.com/serve?blob-key=AMIfv95XeG-ii4aKZsUB5w-ClP0QUhJZa-o5BQRvdqArCCwg0Ueb13-wAfmyNHgaDdTaFS152_kXkJg5_9386zlfRCDc3fagW7Ekagdd6_VvJl6IscqNkyvVkXKYAqIRe-KqDMpjG-cW";
createdDate = "10-Jun-2010 06:11 PM";
description = "photos took in Dubai";
lastViewedDate = "10-Jun-2010 06:11 PM";
modifiedDate = "10-Jun-2010 06:11 PM";
numberOfPhotos = 10;
}
)
You need to create a subclass of NSManagedObject and define all needed fields
#interface AlbumInfo : NSManagedObject
#property (nonatomic, retain) NSNumber * albumId;
#property (nonatomic, retain) NSString * albumName;
#property (nonatomic, retain) NSString * coverPhotoURL;
#property (nonatomic, retain) NSDate * createdDate;
#end
then you need to call this code
context = /* Get the NSManagedObject context */
AlbumInfo *item = [NSEntityDescription insertNewObjectForEntityForName:#"AlbumInfo" inManagedObjectContext:context];
item.albumId = #"some value from json";
/* ... so on ...*/
NSError *error = nil;
if (context != nil) {
if ([managedObjectContext hasChanges] && ![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
I Suppose you created an Entity That fits the data you need to store.
Then Create NSEntityDescription and NSManagedObject to start load your object into Core Data.
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"ALbum" inManagedObjectContext:self.managedObjectContext];
NSManagedObject *newAlbum = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:self.managedObjectContext];
Then you should set values into managed object
[newPerson setValue:#"photos took in Dubai" forKey:#"description"];
[newPerson setValue:#" UAE" forKey:#"albumName"];
Last Step you should save These changes like this
NSError *error = nil;
if (![newAlbum.managedObjectContext save:&error]) {
NSLog(#"Unable to save managed object context.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
I would like to suggest importing data using MagicalRecord an excellent wrapper for CoreData. It provides convention over configuration method of data import.
The importing feature is capable of mapping the data even if the properties are different in your json model compared to entity or if you would like to convert date represented in string to NSDate
To avoid duplicate entries, MagicalRecord makes it a convention to use a uniqueID. i.e., It expects a uniqueID property for an entity Album like albumID, Employee - employeeID etc. So one need to model it as per convention. As per your case model would be like this.
You can note that in the User Info of albumID, the property is mapped to albumId. Similarly the createdDate is not holding a string value but a NSDate.
A dateFormat is set to the UserInfo of createdDate
Now the configuration part is completed. You can import the date to CoreData as in
NSData *jsonData = //Data from web service;
NSArray *albums = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:nil];
for (id obj in albums) {
[Album MR_importFromObject:obj];
}
Check if data is properly imported using
NSArray *savedAlbums = [Album MR_findAll];
Album *album = [savedAlbums lastObject];
NSLog(#"Created Date : %#",album.createdDate);
I'm fairly new to coredata and have been stuck on an issue.
Any assistance will be greatly appreciated.
Information:
I have a core data app with to entities;
List and Task.
List and Task have a one-to-many relationship.
Task.h
#class List;
#interface Task : NSManagedObject
#property (nonatomic, retain) NSString * task;
#property (nonatomic, retain) NSString * note;
#property (nonatomic, retain) List *list;
#end
List.h
#class Task;
#interface List : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSDate * dateCreated;
#property (nonatomic, retain) NSNumber * sortOrder;
#property (nonatomic, retain) NSSet *task;
#end
#interface List (CoreDataGeneratedAccessors)
- (void)addTaskObject:(Task *)value;
- (void)removeTaskObject:(Task *)value;
- (void)addTask:(NSSet *)values;
- (void)removeTask:(NSSet *)values;
#end
I create lists using;
NSManagedObjectContext *context = [self managedObjectContext];
if ([self.listTextField.text length] == 0) { // Quit here if no text is entered
[self dismissViewControllerAnimated:YES completion:nil];
return;
}
// Create a new list.
// Create an NSManagedObject for our database entity.
list = [NSEntityDescription insertNewObjectForEntityForName:#"List" inManagedObjectContext:context];
// Add the new task to the object (which in turns adds to our database).
[list setValue:self.listTextField.text forKey:#"name"];
// Get current date and time.
NSDate *todayDate = [NSDate date];
// Add the date to the object (which in turns adds to our database).
[list setValue:todayDate forKey:#"dateCreated"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
I can update already created lists using the code below with cellaccessorybuttontapped;
NSManagedObjectContext *context = [self managedObjectContext];
if ([self.listTextField.text length] == 0) {
// Delete object from database.
[context deleteObject:self.list];
NSError *error = nil;
// Save the action to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
} else {
// Update existing task.
[self.list setValue:self.listTextField.text forKey:#"name"];
// Get current date and time.
NSDate *todayDate = [NSDate date];
// Add the date to the object (which in turns adds to our database).
[list setValue:todayDate forKey:#"dateCreated"];
NSError *error = nil;
// Save the action to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
[self dismissViewControllerAnimated:YES completion:nil];
I can then navigate into a list.
My question is how can I then create a task for the list I have just navigated into?
It's been 2 days and I've not been able to find anything on Google.
As advised I have added;
#property (strong, nonatomic) List *selectedList;
I now have this as my Save method
NSManagedObjectContext *context = [self managedObjectContext];
// Saving a new task.
Task *task = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:context];
task.task = self.taskText.text;
task.note = self.noteText.text;
task.list = self.selectedList;
NSLog(#"The selected list is: %#", [self.selectedList description]);
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self.selectedList addTaskObject:task];
[self dismissViewControllerAnimated:YES completion:nil];
The new task is created but it is created in all lists.
Is it possible that this is working and I'm not fetching tasks based on their list?
This is my fetch request when I navigate into a list:
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create a fetch request.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Create an entity so fetch the data from.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Task" inManagedObjectContext:self.managedObjectContext];
// Set the entity of the fetch request.
[fetchRequest setEntity:entity];
// Set the amount to be fetched at a time
[fetchRequest setFetchBatchSize:20];
// Create a sort descriptor.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"task" ascending:NO];
// Attach the sort descriptor to the fetch request.
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create a fetch result controller using the fetch request
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
theFetchedResultsController.delegate = self;
// Perform fetch.
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Handle error.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
return fetchedResultsController;
That is what these methods in your entity header are for:
- (void)addTaskObject:(Task *)value;
- (void)removeTaskObject:(Task *)value;
- (void)addTask:(NSSet *)values;
- (void)removeTask:(NSSet *)values;
You'll create a new task entity:
Task *t = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:context];
Then complete the fields in it as you did with your list object:
t.task = #"Whatever";
t.note = #"Whatever Note";
t.list = currentlySelectedListItem; // whatever that happens to be -- it will be a (List *)something
Then, you want to add the task object to the list:
[currentlySelectedListItem addTask:t];
Then save the context & you're done.
Key thing here is that you're effectively updating the List object by adding a task to the set of Task values contained in the NSSet. And t.list is going to contain a pointer to the parent List object.
Looks to me like you have it laid out just fine (I'm assuming the:
#property (nonatomic, retain) List *list;
Is a relationship to the parent List and not just another value you have defined; that looks to be the case).
You need to keep track of the list that you have navigated into. One way of doing it would be to create a property called as parentList like so
#property(nonatomic,strong) List *parentList
in the view controller you create a task in. And just before navigating to the view controller set this property.
In the task view controller you do a insert similar to the List object using Task *reqdTask = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:context]; and then set all the values once say Save button is pressed.
[parentList addTaskObject: reqdTask];
and ur done. This will create a Task in the task entity and map it to the List entity. Hope this helps.
**EDIT***
You need to do this [parentList addTaskObject: reqdTask]; before saving your context.
Add this in the NSFectResultsController
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"list = %#",self.parentList]];
so it will be something like this
// Create a fetch request.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Create an entity so fetch the data from.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Task" inManagedObjectContext:self.managedObjectContext];
// Set the entity of the fetch request.
[fetchRequest setEntity:entity];
// Set the amount to be fetched at a time
[fetchRequest setFetchBatchSize:20];
// Create a Predicate.
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"list = %#",self.parentList]];
//continue
this will bring the task associated with the selected list.
I have an issue with Core Data. I have an entity called "Group", and other entity called "Contact". These entities have a relationship "many to many" between them, because a group could have many contacts (members of the group), and a contact could have many groups (be a member of many groups).
So, this is the relationship:
Group <<----->> Contact
What I need is insert contacts (new or existing) as members of existing groups.
What is my issue? Well...I am able to insert them if I create a new contact, but if the contact already exists, Core Data does not overwrite and save it.
Here is my code:
- (void)saveMemberInGroup:(Message *)message
{
GroupInfo *groupInfo = message.group;
UserInfo *member = message.user;
AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = [app managedObjectContext];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:CONTACT_ENTITY
inManagedObjectContext:context];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"ident == %#", [member ident]]];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
//Here I check if the contact exists.
Contact *contact;
if ([fetchedObjects count] == 0) {
contact = [NSEntityDescription insertNewObjectForEntityForName:CONTACT_ENTITY
inManagedObjectContext:context];
[contact setIdent:[NSString stringWithFormat:#"%#", [member ident]]];
} else {
contact = [fetchedObjects objectAtIndex:0];
}
//Now I make a new request for the group.
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription entityForName:GROUP_ENTITY
inManagedObjectContext:context];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"ident == %#", [groupInfo ident]]];
[fetchRequest setEntity:entity];
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
Group *group = [fetchedObjects objectAtIndex:0];
//Now, I overwrite the group and the contact.
[[group members]addObject:contact];
[[contact memberGroups]addObject:group];
[context save:&error];
}
Here is my Group entity:
#interface Group : NSManagedObject
#property (nonatomic, retain) NSString *ident;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableSet *members;
#end
And my Contact entity:
#interface Contact : NSManagedObject
#property (nonatomic, retain) NSString *ident;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableSet *memberGroups;
#end
NOTE:
This is really weird...when I check if I have updated the objects successfully, everything seems to be ok, both groups and contacts have their NSSet updated rightly, but when I restart the app (I mean, finalize and start it again, or press "play" in XCode), Core Data only has saved the relationship with the new contacts created!!.
Example:
Create Contact1 and save in Group1.
Create Contact2 and save in Group1.
Existing Contact1 and save in Group2.
Create Contact3 and save in Group2.
If I check Core Data when my app is running, I will have in Group1 (Contact1, Contact2), and in Group2 (Contact1, Contact3).
Then I restart the app and check again Core Data, and now I have in Group1 (Contact1, Contact2), and in Group2 (Contact3)!! Core Data has lost the relationship between Contact1 and Group2, and this happens every time I have a relationship between a group and an existing contact.
What I am doing wrong??
Thanks a lot!.
You should create the class files for your entities with "Editor -> Create NSManagedObject
Subclasses ..." from the Xcode menu. A to-many relationship is represented by an NSSet,
not a NSMutableSet, so you cannot modify it directly by adding an object.
Once you have done that, you can add an additional object to group.members with
[group addMembersObject:contact];
or alternatively
[contact addMemberGroupsObject:group];
Core Data updates inverse relationships automatically, so it does not matter
which one you choose.
I am struggling with CoreData. I'd like to fetch the following object:
#interface Email : NSManagedObject {
}
#property (nonatomic, copy) NSString *email;
#property (nonatomic, copy) NSString *contact;
#end
..and put the result inside a NSMutableArray, but the NSString contents, (not NSManagedObjects!). This is because I am using json-framework and that engine does not allow NSManagedObjects to be passed.
These lines fetch perfom the fetch from CoreData
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Emails" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:#"email"]];
NSError *error = nil;
NSMutableArray *fetchedObjects = [[NSMutableArray alloc] init];
fetchedObjects= [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
flight.emails=fetchedObjects;
The 'flight' object is declared as follows:
#interface Flight : NSObject {
NSMutableArray *_emails;
}
#property (nonatomic, retain) NSMutableArray *emails;
With this code I am getting CoreData faults. I tried some other implementation variants, but never actually managed to have NSString objects in flight.emails, but always NSManagedObjects. What I tried was to implement a dedicated getter function in the Email NSManagedObject that copies the fetched NSString and returns the copy.
I get the idea that this is kind of a common problem, however, my research has not led to a solution on this one here.
Thanks,
Peter
From experience, setPropertiesToFetch: only works when requesting the returned objects be dictionaries. So a couple of problems in your code:
You are asking for specific property (email), but have not set the return type to dictionary. Take a look at setResultType:.
You still need to take the result and extract the email objects from it. You cannot just assign the resulting array to your emails property.
Try this:
[fetchedRequest setResultType:NSDictionaryResultType];
NSArray* results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if(error != nil || results == nil) return;
flight.emails = [results valueForKeyPath:#"email"];