Saving and Deleting NSManagedObject & NSManagedObjectContext - ios

Three Questions but they are all related. If you like I can divide them into three questions so that you can more credits. Let me know if you'd like for me to do that.
I have the following code that allows me to access NSManagedObject
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext]];
NSArray *objectArray = [managedObjectContext executeFetchRequest:request error:&error];
if(objectArray.count==0){
letsMeet = (LetsMeet *) [NSEntityDescription insertNewObjectForEntityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext];
} else{
letsMeet = (LetsMeet *)[objectArray objectAtIndex:0];
}
The code above allows me to save and retrieve attributes. i.e. I can access letsMeet.attribute to save and fetch.
Question 1: How do I delete and start a brand new managedObjectContext. i.e. User has a form that he's been filling out between the scenes. Everything is saved to CoreData from each scene as the user hits the Next button on the navigation Controller. After going through several screens, the user wants to cancel the form. At this point I would like to delete everything that has been saved thus far. Code example please.
Question 2: Lets say the user gets towards to end of the form and decides to save the form for later retrieval. How do I save a copy of the entire form as one object in Core Data. Code example please.
Question 3: How do I retrieve that saved object from Core Data at a later time and display what all the user had saved? Code example please.

To delete you just need to delete letsMeet object from NSManagedObjectContext.
NSError *error;
[managedObjectContext deleteObject:letsMeet];
[managedObjectContext save:&error];
Since you always have only one object, getting the reference of letsMeet is not a problem. You can do as you did in your code.
Update:
And you don't need to delete the managed object context. It just a space to deal with your objects. More explanation at the end of question.
2. If the LetsMeet entity is modeled in a way that all the form elements are attributes of LetsMeet, when you save the managedObjectContext after creating a LetsMeet object as you have done in code, this will be saved as a single object.
3.You already know how to retrieve an object as thats what you are doing in the code. Everything becomes easy as you are only using one object.
In the case of multiple objects to get a the unique object, you should either implement a primary key,(maybe formID, i.e; add another attribute to LetsMeet) or you should know what the objectId of each object is and then set the predicate of your fetch request accordingly.
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:letsMeet];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"formId like %#", formId];
[request setPredicate:predicate];
NSArray *resultsArray =[managedObjectContext executeFetchRequest:request error:&error];
If your formId is unique, this will return you a single object array.
But if you are using core-data for only handling one object, you could've used NSUserDefaults or write to a plist File to do this. This is kind of overkill.
Update:
To get the objectId of a NSManagedObject:
[letsMeet objectId];
ManagedObjectContext is like a whiteboard. The object you have inside the array, the object inside managed object context, its all the same. You can change the objects, add object, delete object etc. Only thing is whatever is the current state of the object(s) when you do a [managedObjectContext save:] , that is written to disk.

Related

RestKit and Managed Object Contexts

I'm trying to best format my project's use of RestKit and Core Data. There are a couple of things that I've got working, but I have a feeling they are implemented poorly and potentially thread unsafe... I have an object that manages all of my data transfer, storage, etc., which has a function that sets up restkit. I have an instance variable that I use for RKObjectManager, and in this setup function I create the objectStore, setup all the attribute mappings, create the persistent store, etc., - all of the normal restkit setup code. Outside of this function, the only thing available to this object is the _objectManager instance variable, which I've been using for NSFetchRequests and such.
There are two things I want to make sure I'm implementing properly, fetching managed objects, and savings changes to managed objects.
If I want to update a property on an object, I've been doing this:
object.whatever = #"something here";
NSError *error;
if (![object.managedObjectContext save:&error]) {
// log the error here
}
Is this the proper way to update/save a property on an object? Is accessing the object's managed object context directly save to do at any point in the code, or is this something that should be done only in the background/foreground? My current implementation could potentially have this called in both the background and foreground and I just want to make sure this is acceptable.
When I want to fetch an object, I wrote a function that takes an entity name, an array of predicates, and a sort descriptor as parameters so it can be reused:
NSManagedObjectContext *managedObjectContext = // I DONT KNOW WHAT TO PUT HERE! //
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
[fetchRequest setPredicate:compoundPredicate];
NSError *error;
NSArray *fetchedRecords = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
// log error
}
// if we were given a sort descriptor, sort the array appropriately
if (sortDescriptor) {
fetchedRecords = [fetchedRecords sortedArrayUsingDescriptors:#[sortDescriptor]];
}
return fetchedRecords;
My problem here is creating/accessing the correct managed object context. How should I do this? Do I access some property on the RKObjectManager I created before such as:
_objectManager.managedObjectStore.mainQueueManagedObjectContext
or is that not thread safe because its for the main thread? What can I do to make sure I'm using the right managed object context, and that it is thread safe? I was using:
_objectManager.managedObjectStore.persistentStoreManagedObjectContext
but I was told this was definitely not best practice and was not thread safe, so I'm trying to determine the best solution.
EDIT - perhaps I can call this function to get the context whenever I want to fetch objects?
- (NSManagedObjectContext *)getManagedObjectContext {
if ([NSThread isMainThread]) {
return _objectManager.managedObjectStore.mainQueueManagedObjectContext;
}
else {
return [_objectManager.managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType tracksChanges:YES];
}
}
For saving, instead of this:
if (![object.managedObjectContext save:&error]) {
you should do:
if (![object.managedObjectContext saveToPersistentStore:&error]) {
so that the changes are persisted right up the chain to the on-disk store. You should only be doing this on the thread that created / fetched the managed object (thus the thread ownership of the MOC is maintained).
Foreground / background isn't important so much as which MOC is used by each thread. If the MOC thread ownership is respected then you should be fine.
Same applies to fetching. For UI updates, you must use the main thread and the mainQueueManagedObjectContext. You should never directly use the persistentStoreManagedObjectContext. For arbitrary background threads you should be asking the managed object store to create a new child managed object context for you and using that.

How to create CoreData entity in one class and access in another?

I am trying to learn the workings of Core Data. So I created an entity with attributes, got the xcdatamodel file and the .h and .m files for my entity. Great.
I also wrote the following code in my app delegate to add data to my entity:
TestEntity *newEntity = [NSEntityDescription
insertNewObjectForEntityForName:#"TestEntity"
inManagedObjectContext:self.managedObjectContext];
newEntity.entityName = #"temp";
NSError *savingError = nil;
if([self.managedObjectContext save:&savingError]){
NSLog(#"Success");
}
else{
NSLog(#"Fail");
}
so far so good. but now i want to access this data, i.e. the entityName, in my main view controller. This is where I am lost. I read source code samples, and most of them say do the following
NSError *requestError = nil;
NSFetchRequest *fetchrequest = [[NSFetchRequest alloc] initWithEntityName: #"TestEntity"];
NSArray *entities = [self.managedObjectContext executeFetchRequest: fetchrequest error:&requestError];
This should give me an array called entities full of TestEntity objects. So I should be able to access the name by saying entities.entityName.
But i get an error saying that managedobjectcontext is not a property of this view controller. That makes sense to me, but i cannot figure out what the solution to this is? Do I just have to implement the necessary coredata properties/methods in each class I want to use core data in?
This isn't really a Core Data question, it's a basic Objective-C question. In order to use self.managedObjectContext, self must be an instance of a class that has an instance variable (i.e. property in most modern ObjC) called managedObjectContext. And you need to assign a value to that property if you want to use it.
Adding the property is easy. Just declare it on the class as a property of the appropriate type (here, an NSManagedObjectContext). Assigning a value to it depends on the overall structure of your project. In Xcode's project templates the app delegate creates a managed object context. The app delegate also typically has a reference to one or more view controllers. If you're using this common structure, one way to assign the view controller's managed object context property from somewhere in the app delegate. Something similar to
self.mainViewController.managedObjectContext = self.managedObjectContext;
If your app's architecture is different, you'll need to do something else, but what that is will depend on the details of the app.
You have to refer to your AppDelegate. Do it like this:
AppDelegate *del = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = del.managedObjectContext;
NSError *requestError = nil;
NSFetchRequest *fetchrequest = [[NSFetchRequest alloc] initWithEntityName: #"TestEntity"];
NSArray *entities = [self.managedObjectContext executeFetchRequest: fetchrequest error:&requestError];

Core data and multiple entites

Having a really hard time grasping the way core data works, and I'm hoping I can get some very basic help here.
I have two entities:
Profiles<-->>Events
I have successfully figured out how to add profiles, view profiles in table view and view events for a profile in a table view via a predicate fetch.
Now, here is where I am lost. Lets say I want to update an event in the Event entity. Do I have to do a fetch with predicate to create a Profiles object before I update the Event entity? Or can I just update the Event entity and somehow tell it which Profile it is associated with via the relationship?
Here is where I have hit the log jam:
// add new event
//NSLog(#"Adding New Event");
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Events"];
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"ANY profile.profilename=[cd] %#",[self profilename]];
[fetchRequest setPredicate:predicate];
self.events = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
//insert event info
NSManagedObject *eventInfo = [NSEntityDescription insertNewObjectForEntityForName:#"Events" inManagedObjectContext:self.managedObjectContext];
///////// THIS IS WHERE I NEED HELP
}
// save the context
NSError *error = nil;
if (![managedObjectContext save:&error]){
NSLog(#"Error! %#",error);
}
I'm about ready just to create a flat file and work with that! It's driving me nuts!
EDIT - CHANGED CODE BELOW ***********************
// add new event
//NSLog(#"Adding New Event");
Events *newEvent = (Events *)[NSEntityDescription insertNewObjectForEntityForName:#"Events" inManagedObjectContext:managedObjectContext];
newEvent.eventdesc=self.eventDescTextField.text;
NSString *wkst = eventDescTextField.text;
NSNumber *wk = [NSNumber numberWithInteger: [wkst integerValue]];
newEvent.weeksout = wk;
So now I know I need to tell the Event entity to use the current profile..how do I access the relationship?
Looking at the code you've provided, I think you have a misconception about Core Data.
It looks like you are trying to get all the events related to a profile. You don't need to create ond perform a fetch request for this. Core Data is an object graph. Which means that if you have an object in an a managed object context, you get its related objects via it's relationships, you don't need to run a fetch request.

CoreData in a tableCell Causes a stutter/lag IOS/xCode

Good Morning.
I have a problem when I run my app on my device, it lags/stutters when I scroll in the main tableView.
I've narrowed the problem down to a call to core data from inside my tableCell
--In cell for row at indexPath
person is a custom class and contact manager is my file with all my calls to core data and manipulating data
person.contactSelected = [contactManager checkContactSelectedStatus:person];
--In my contactManager file the call goes to this function.
and just updates the contacts selected status (when the user presses a button to change from being allowed in the call or not in the call)
-(NSNumber *) checkContactSelectedStatus:(ContactPerson *)person{
SWNAppDelegate *delegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [delegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:context];
NSFetchRequest *req =[[NSFetchRequest alloc]init];
[req setEntity:entity];
NSPredicate *pred =[NSPredicate predicateWithFormat:#"(recordID like %#)",[person.recordID stringValue]];
[req setPredicate:pred];
NSError *error;
NSManagedObject *checkStatus = [[context executeFetchRequest:req error:&error] objectAtIndex:0];
person.contactSelected = [checkStatus valueForKey:#"isSelected"];
return person.contactSelected;}
Is there an easy way to throw this into a Queue? I have read and tried to figure out how to send a NSManagedObject to queues, but when I create a child of the Parent MoC, It can not find the Entity "Contact". I don't know if there is a simpler way to do it or not!?
Thanks for your time, and
WhatWasIThinking!?!?!
Yes, this is really inefficient code. The fetch has to be done multiple times, i.e. for each cell as it becomes visible.
You should instead use an NSFetchedResultsController which is especially designed to work with table views. It will decide the appropriate number of trips to the store for fetches and optimize for speed and memory footprint.
Also, you will most likely use significantly less code.
Besides, a predicate string like recordID like %# does not make much sense. If you are using IDs they should be unique so you can index them and look them up really fast. LIKE, just like string functions such as CONTAINS are very slow in comparison.

Passing a core data object to a sub view on didSelectRowAtIndexPath

I have a core data setup that has 2 database entities. For the sake of names I'll call them Primary and Secondary. Secondary only belong to one Primary (relationship is setup). In my main view that lists the Primary objects in a table I retrieved them and put them in an PriObject Class which stores it's properties (including the managed object ID). The PriObject is then added to a mutable array (priArray), which is then used to fill the table with the data. All works ok so far. When I then click on the row I can log the PriObject.moID.
I can't figure out how to lookup that object in the database so I can then add Secondary objects to it. I can't do it by name because some Primary might have the same name.
I need to work out how to get an object back from either the URI or the ID. I have the ID so I can generate the URI if I need to.
Can't get my head around it at all and any examples I have found while looking don't cover what I need. What options are there?
EDIT: I am currently getting all my objects with the following.
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Primary"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
if ([objects count] == 0) {
NSLog(#"Nothing found");
} else {
NSLog(#"Something found");
}
How can I change this for just the one using:
ObjectWithID:
Call the objectWithID: method on your NSManagedObjectContext instance to retrieve the instance.
As an aside, it seems like you are making things harder on yourself with this PriObject class, it seems to be a wrapper around your NSManagedObject instances, is that right? I'd just use NSManagedObject subclasses directly, personally.

Resources