I am using XMPP Framework's Core Data message storage and I want to add to my app another Core Data Entity, I create .xcdatamodeld and create Entity, I add code to my AppDelegate, but I get error. Can it be because I use XMPP Framework's Core Data message storage and how to fix it?
NSManagedObjectContext *context = [self managedObjectContext_messageList];
NSManagedObject *contexNew = [NSEntityDescription insertNewObjectForEntityForName:#"UserProfileEntity" inManagedObjectContext:context];
[contexNew setValue:#1 forKey:#"id"];
[contexNew setValue:#"name TEST" forKey:#"name"];
[contexNew setValue:#"test YEP" forKey:#"test"];
NSError *error;
if ([context save:&error])
{
NSLog(#"%#", error.description);
}
It crashes on on second line with
'+entityForName: nil is not a legal NSManagedObjectContext parameter
searching for entity name 'UserProfileEntity''
That error means that nil is not a legal thing to pass for the managed object context. So, did you inject the context in the correct manner? I mean that the following line
NSManagedObjectContext *context = [self managedObjectContext_messageList];
should have a counterpart like
yourController.managedObjectContext_messageList = theContextYouWantToInject;
or
[yourController setManagedObjectContext_messageList:theContextYouWantToInject];
If you are using segues here a useful discussion: '+entityForName: nil is not a legal NSManagedObjectContext parameter - Core Data.
In addition, are you sure you are providing a valid store coordinator? For further references take a look to The Core Data Stack.
Related
I have an NSManagedObject (User) in database. Then I'm trying to fetch that object from database and update field firstName:
NSFetchRequest *fetchR = [NSFetchRequest fetchRequestWithEntityName:#"User"];
NSError *err = nil;
NSArray *allUsers = [self.managedObjectContext executeFetchRequest:fetchR error:&err];
TMUser *profile = allUsers.firstObject;
[profile setValue:#"Username" forKey:#"firstName"];
[self.managedObjectContext save:&err];
if (err) {
NSLog(#"Error: %#", err.localizedDescription);
}
The code passes without errors. But if I relaunch my app, fetch request retunrs user without updated field "firstName". I have only 1 NSManagedObjectContext. All Core Data stack was initialized successfully. After fetch my user is:
Printing description of allUsers:
<_PFArray 0x14ed6600>(
ID:3451
firstName:Johnatan
lastName:Hike
phone:380995046960
email:igor#email.com
language:en
)
For some reason object changes wasn't registered in context(Context hasChanges = NO before save). What am I doing wrong? Please, help
I think you are not saving the master context.
Please check that you call:
[managedObjectContext save:&error];
on all child contexts that save the data,
and after that on the master context as well.
You have one global function(in AppDelegate) saveContext which saves everything and which I can call from anywhere safely.
I solved my problem. I recreated NSManagedObject subclass from xcdatamodeld scheme and it works. I found that if I add another properties(readonly etc.), not related to data model scheme or change property type from NSNumber(aka bool) to BOOL, it stops updating existed objects in database.
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.
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];
I am new to the IOS programming, currently learning core data, I went into running the code where i need to save only specific objects in core data. So how can i do that?
According to the scenario, i have data from server as well as local storage (core data), but when user close the app (went to background) I want to store the data in the server(if net available) if not then in the local storage (but selected only - means specific data should be stored, there are objects which came from online server which i dont want to store on local).
Please let me know any solution if possible.
Regards
Nisar Ahmed
I see two ways to achieve this:
Iterate through inserted and updated objects and revert those you do not wont to save. Inserted objects should be deleted, updated should be refreshed:
for (NSManagedObject* obj in [self.managedObjectContext insertedObjects]) {
if (/*Shouldn't be saved*/) {
[self.managedObjectContext deleteObject:obj];
}
}
for (NSManagedObject* obj in [self.managedObjectContext updatedObjects]) {
if (/*Shouldn't be saved*/) {
[self.managedObjectContext refreshObject:obj mergeChanges:NO];
}
}
Create separate managed object context. Recreate objects that you want to save in new context and then save it.
NSManagedObjectContext* newContext = [[NSManagedObjectContext alloc] init];
[newContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
for (NSManagedObject* obj in objectsWantToSave) {
NSEntityDescription* entity = [obj entity];
NSDictionary* valuesByKeys = [obj dictionaryWithValuesForKeys:[[entity attributesByName] allKeys]];
NSManagedObject* objCopy = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:newContext];
[objCopy setValuesForKeysWithDictionary:valuesByKeys];
}
[newContext save:NULL];
The second approach is better for my opinion.
Have a look into UIManagedDocument - http://developer.apple.com/library/ios/#documentation/uikit/reference/UIManagedDocument_Class/Reference/Reference.html
It takes care of a lot of the boilerplate involved in using core data.
I'm using 2 managed object contexts for efficiently important a large data set in the background. I'm ensuring I'm only using 1 managed object context at a time in the thread.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
MetricType *metricType = [self metricTypeForName:self.metricName fromContext:context];
NSLog(#"metric context %#", [metricType managedObjectContext]);
[self processDataInContext:context forMetric:metricType];
In the snipped of code above, the NSLog correctly prints out the address of the managedObjectContext i'm using. I then go on to processDataInContext - which is just a private method to interate over a json data array and add objects. Each object has a relationship to the MetricType.
However, when I go to associate them
metric.metricType = metricType;
I get the error: Illegal attempt to establish a relationship 'metricType' between objects in different contexts.... even though I'm ensuring I don't do this.
When I do a log output before this line:
NSLog(#"My Context %#", context);
NSLog(#"metric context %#", [metricType managedObjectContext]);
The metricType context returns nil!!
How has it become nilled? I didn't nil it and this seems to be the reason its complaining.
I figured it out
[context reset];
..was being called every 50 records, which of course was removing my metricType object from the context and I wasn't re-fetching it.
The way you initialize metricType looks fishy. What does [self metricTypeForName:fromContext:] actually do to return a MetricType instance? Are you creating a new MetricType using [[MetricType alloc]init]? I suspect you are returning something in an auto-release pool