I have a integer named marbles, and am trying to save it into an array using the following code:
[records setValue:marbles forKey:#"marbles"];
With this code, I get the warning:
warning: Semantic Issue: Incompatible integer to pointer conversion sending 'int' to parameter of type 'id'
So, How do I set the value for an NSInteger.
Next Question, How do re-upload the array into core data? I fetch the array, make changes, and how do I apply those changes back to Core Data?
Thanks
Initialize an NSNumber (which is what CoreData is expecting) with your integer:
NSNumber *marbleNumber = [NSNumber numberWithInt:marbles];
[records setObject:marbleNumber forKey#"marbles"];
Or:
[records setMarbles:[NSNumber numberWithInt:marbles]];
To persist your changes, you save your context:
NSError *error;
[myManagedObjectContext save:&error];
//handle your error
NSArrays will only take objects, so the first step is to turn your NSInteger into a NSNumber using this method:
+ (NSNumber *)numberWithInt:(int)value
so:
NSNumber *myNumber = [NSNumber numberWithInt:marbles];
and then you can do:
[records setValue:myNumber forKey:#"marbles"];
Basically once you fetch the data, you get a managedObjectContext, think of it as a drawing board, and any changes (including adding or deleting new objects), you make to this objects may be saved again to CoreData using something like this:
NSError *error;
if (![context save:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
Where context is the context you would get with your NSFetchedResultsController. Which you can get like this:
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
I would recommend taking a look at the Core Data programming guide
- (id)primitiveValueForKey:(NSString *)key;
- (void)setPrimitiveValue:(id)value forKey:(NSString *)key;
use NSNumber in place of (id)value
Related
I am confused of the core data's logic.
I treated the core data as a database, and core data methods as a SQL query.
When I tried to update some object in a core data with a local memory's object, I found that I can make the feature just with below code:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Book" inManagedObjectContext:_managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"identifier == %#", theBook.identifier];
[request setPredicate:predicate];
NSError *error;
Book *book = [[_managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (error) {
NSLog(#"Error getting book to update: %#", error);
abort();
}
// NSLog(#"%d", book == theBook);
error = nil;
if (![_managedObjectContext save:&error]) {
NSLog(#"Error saving the book: %#", error);
abort();
}
theBook is the object which I want to update the core data's object with.
And I found that the log message says the two objects are same...
I fetched the request and didn't do anything but it works. Why does it work?
At first Core Data is not a database - it's an object graph, which could be saved to some kind of storage.
If your objects in there have unique identifier, then fetching with the same identifier value gives you the same object. Core Data does not create different instances of managed objects for the same entities (say book with identifier=7), so everywhere you'll deal with the same object, doesn't matter if you fetch it 15 times, you'll always end up with the same one.
I have a coredata project that I'm trying to programmatically update a number.
I'm retrieving objects from CoreData and then storing it into an array.
Then, I'm looping through that array to see if the current user's IP is present in the database and trying to update the number of times accessed for that specific array.
The problem is, it's updating all the objects, not just the current object in the looped array.
First, I get the info from core data like so:
- (void)fetchRecords {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"IPAddr" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"ipDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setIpArray: mutableFetchResults];
}
Now, I'm trying to find if the current User IP is present in the fetched results, and if it's present, update the number of times accessed:
// see if the ip is present and update if necessary
-(void)ipPresent {
NSString * theCurrentIP = [self getGlobalIPAddress];
for (IPAddr *allips in ipArray)
{
if ([allips.ipNum isEqualToString:theCurrentIP]) {
NSLog(#"The IP %# was found.", theCurrentIP);
// update the ip
NSError *error = nil;
NSNumber *ipToUpdate = allips.ipAccess;
NSNumber *addIpAccess = [[NSNumber alloc] initWithInt:1];
NSNumber *updateIpAddress = [NSNumber numberWithFloat:([ipToUpdate floatValue] + [addIpAccess floatValue])];
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
if ([self.managedObjectContext save:&error]) { // write to database
NSLog(#"The IP Was Updated from %# to %#", ipToUpdate, updateIpAddress);
} else if (![self.managedObjectContext save:&error]) {
NSLog(#"failed with error: %#", error);
}
break;
} else {
NSLog(#"The IP %# was NOT found.", theCurrentIP);
}
}
}
I'm pretty sure the issue is with this line:
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
Again, it's updating ALL the entities and not just the one in the current loop.
Indeed. You are using the wrong method. self.ipArray is a NSMutableArray.
The method
- (void)setValue:(id)value forKey:(NSString *)key
is used for Key-Value Coding (which is what makes it work for Core Data objects), but when applied to an array, it will invoke setValue:forKey: on each entry in the array.
Now, you can see that you could also call setValue:forKey on the one single array element allips since its property is obviously KVC compliant -- otherwise you would be having a different problem, not see the values being set.
Note, that you could also just assign the property...
allips.ipAccess = updateIpAddress;
EDIT
Sorry, probably should have read slower... You do understand that you don't have to use a mutable array, right? You are not actually changing the array, just the elements in the array. An immutable collection means that the collection contents can not change, but when you have a pointer to an object, as long as that object is not immutable, you can still mutate its properties.
Thus, if you had an immutable array of Foo objects, you could do this...
for (Foo *foo in myImmutableArray) {
Bar *bar = [self getSomeNewBar];
[foo setBar:bar];
// If Foo is KVC compliant, you can do this too...
[foo setValue:bar for Key:#"bar"];
}
If, however, you call setValue:forKey on the array, it will be invoked for each element of the array. Note, that setValue:forKey is actually declared in the immutable NSArray.
EDIT
That comment was hard to read.
The core data object is just another object. It looks like you have subclassed it, and provided it with properties for the attributes. Just replace
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
with
[allips setValue:updateIpAddress forKey:#"ipAccess"];
or
allips.ipAccess = updateIpAddress;
Either of those should modify your core data object, as they would any object that had a read/write property named "ipAccess"
Assuming, of course, that I didn't read it wrong again... and allips is your core data object...
I have CoreData in my app, with an Entry class, which contains an NSOrderedSet of Media classes.
I then have this code, for adding a new Media item to the NSOrderedSet:
-(void)addImage:(UIImage *)image isInPhotoLibrary:(BOOL)isInPhotoLibrary {
Media *media = [[Media alloc] init];
media.type = #"Image";
media.originalImage = UIImageJPEGRepresentation(image, 1.0);
media.isInPhotoLibrary = [NSNumber numberWithBool:isInPhotoLibrary];
[self addMediaObject:media];
}
Will this automatically save the changes, or will I have to do it myself. If so, will i then need to pass in a context to do this, or is there another way?
No, this code doesn't have any Core Data references at all.
Is Media an NSManagedObject? If so you need to be creating it like so:
Media *media = [NSEntityDescription insertNewObjectForEntityForName:#"Media" inManagedObjectContext:context];
This will put it in your managed object context.
If you then want to persist it, you will need to call save: on the managed object context.
EDIT ALSO....
In your Entry class, you will probably have a generated method that you use to add objects to the NSSet. It will be in a category (CoreDataGeneratedAccessors) on the Entry header file
- (void)addMediaObject:(Media *)value;
No it won't.. If you want to save changes to Database in Core data you gotta call save function for that.. I assume Media is kind of NSManagedObject class. To save the changes to persistent store you have to call save method . Until then the changes are just temporary present on your scratch board/ ManagedObjectContext.
This is how I save changes:
Worker *worker = (Worker *)[NSEntityDescription insertNewObjectForEntityForName:#"Worker" inManagedObjectContext:self.managedObjectContext];
worker.name=txtContact.text;
worker.address=txtAddress.text;
worker.zipCode=txtZip.text;
worker.city=txtCity.text;
worker.mobile=txtMobile.text;
NSError *error;
if (![managedObjectContext save:&error])
{
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
I'm using Core Data + sqlite as a data cache. The app reads in a file into core data, then uses Core Data to run the app. Saves are done to both the NSManagedObjectContext and to the file. I've noticed, though, if I quit the app and reload it without repopulating the Core Data database, some (but not all) of the data saved using -save: is not being saved to the data store.
Changes to my managed object are all done in a single batch on the main thread, with the -save: message being sent after all changes are completed. The data that isn't being saved is a transformable attribute, the only transformable attribute in the core data object. Here's the code that saves the object:
NSInteger columnIndex = [headers indexOfObject:questionID];
if (NSNotFound != columnIndex) {
// parsedLine is a mutable array already
NSMutableArray *parsedLine = person.dataFromFile.parsedLine;
[parsedLine replaceObjectAtIndex:columnIndex withObject:answer];
person.dataFromFile.parsedLine = parsedLine;
person.questionsAnsweredByPerson = [NSNumber numberWithInt:[FileParser questionsAnsweredInRow:person.dataFromFile.parsedLine withRowHeaders:headers]];
person.address.street.questionsAnsweredByPeopleOnStreet = [NSNumber numberWithInt:[self questionsAnsweredByPeopleOnStreet]];
//NSLog(#"rawLineBefore:\n%#", person.dataFromFile.rawLine);
person.dataFromFile.rawLine = [ReconstructCSV composeCSVLineFromArray:person.dataFromFile.parsedLine];
//NSLog(#"rawLineAfter:\n%#", person.dataFromFile.rawLine);
Voter_SurveyAppDelegate *appDelegate = (Voter_SurveyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSError *error;
NSManagedObjectContext *managedObjectContext = [appDelegate managedObjectContext];
if (![managedObjectContext save:&error]) {
// XXX inform the user there was a fatal error opening the file. Low disk space?
NSLog(#"Unresolved error - could not save managedObjectContext - %#, %#", error, [error userInfo]);
abort();
}
return YES;
}
abort(); is not getting called, so I assume -save; is getting called properly.
I doubt it is related, but after this code is run on the main thread, I perform an NSFetchRequest using a new NSManagedObjectContext on a different thread. Nothing else takes place related to Core Data on other threads.
Why isn't the transformable attribute getting saved?
The problem is that transformable properties don't like Mutable objects. As noted in an answer to this question, an NSMutableDictionary wasn't getting saved. In my case, it was an NSMutableArray.
The following is implemented as a class method in a category on one of my managed object entities, with a bunch of other operations:
+ (NSArray*)newArrayOfAllOtherBibs
{
NSManagedObjectContext* moc = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSString* entityName = NSStringFromClass([self class]);
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
NSError* error;
NSArray* items = [moc executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSMutableArray* bibArray = [NSMutableArray array];
for(int i = 0; i < [items count]; i++)
{
Person* person = [items objectAtIndex:i];
if(![person.isSelf isEqualToString:#"YES"])
{
// crash here!
if([person.bib length] > 0)
// crash here!
[bibArray addObject:person.bib];
}
}
return [bibArray retain];
}
So it is supposed to look at all entities for Person, returned for a very simple fetch, and add all that are not marked as "self" (attribute isSelf, type NSString) to a new array which is returned to the caller. All the other methods for add/delete/find matching are working well.
Using
NSString* entityName = NSStringFromClass([self class]);
is an attempt to make the function more generic so I can cut&paste these functions for other entities as required with less editing.
Person looks like this:
#interface Person : NSManagedObject
{
}
#property (nonatomic, retain) NSString * bib;
#property (nonatomic, retain) NSString * isSelf;
[...]
#end
Question is, what could explain EXC_BAD_ACCESS on the line marked? Analyzer shows nothing. At that point person is clearly OK or I would expect it to die earlier, on accessing .isSelf. (It could be made a boolean but I find having two kinds of boolean, one BOOL and one NSNumber, error prone). If person.bib is nil then calling length on it should still return 0 right? But it can't be nil anyway since "bib" is the key for these entities and one is never created without it. At this stage nothing has been deleted from the store, it's a fresh store that has been saved since last addition.
The cause of the problem:
NSDictionary *resultDict = [responseString JSONValue];
NSString* bib = [resultDict objectForKey:#"bib"];
person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:moc];
person.bib = bib;
[... lots of stuff setting up relationships for person etc.]
NSError *error;
if (![moc save:&error])
{
NSLog(#"Core Data Save error %#, %#", error, [error userInfo]);
}
The fix:
person.bib = [bib copy];
seems like the bib string was not valid at the time the Person entity was saved.
The best thing to do is set NSZombieEnabled in the executable and debug it from there. This way you'll be able to see what is causing the problem.
If you are loading some vars lazily make sure you set them to nil when you release them because if not next time when accessed them. They will not be "skipped", you app will try to use them but since they are not pointing to a valid object it fails and gives you errors like EXC_BAD_ACCESS
Person.bib may not have been loaded yet.
Did you leave the bib property defined as #dynamic and not #synthesize?
Did you override the bib accessor method? If so your override needs to be KVO friendly.