Correct way to modify results array from NSFectchRequest without changing Core Data objects - iphone

I am retrieving a fetch from my core data database and trying to iterate through the data and make changes to the data, if necessary. When I change the data in the results array, it turns out that my database is changing in the back end as well, without doing a save. I am wondering what would be a good practice to use to change the data without affecting the back end data.
Here is the code I change the data with:
self.singleDayDataPointsForGraph = [[self fuelPurchaseDataForTimePeriodInMonths:self.numberOfMonthsForGraphView] mutableCopy];
for (int i = 0; i < self.singleDayDataPointsForGraph.count; i++) {
FuelPurchase *currentFuelPurchase = [self.singleDayDataPointsForGraph objectAtIndex:i];
if (i < self.singleDayDataPointsForGraph.count + 1 && self.singleDayDataPointsForGraph.count >= 2) {
FuelPurchase *purchaseToCompare = [self.singleDayDataPointsForGraph objectAtIndex:i + 1];
NSDate *firstDate = currentFuelPurchase.dateTimeStamp;
NSDate *secondDate = purchaseToCompare.dateTimeStamp;
NSDateFormatter *dateComparisonFormatter = [[NSDateFormatter alloc] init];
[dateComparisonFormatter setDateFormat:#"yyyy-MM-dd"];
if([[dateComparisonFormatter stringFromDate:firstDate] isEqualToString:[dateComparisonFormatter stringFromDate:secondDate]] ) {
float firstValue = [purchaseToCompare.fillSavings floatValue];
float secondValue = [currentFuelPurchase.fillSavings floatValue];
purchaseToCompare.fillSavings = [NSNumber numberWithFloat:(firstValue + secondValue)];
[self.singleDayDataPointsForGraph removeObjectAtIndex:i];
}
}
The fuelPurchaseDataForTimePeriodInMonths: method is what performs the fetch and returns an NSArray of results. self. singleDayDataPointsForGraph is an NSMutableArray that stores the results array as a mutable copy. This method basically checks two entries in the database to see if they are the on the same day and if they are, then it adds the fuel purchase amounts to each other and deletes one of the records. I don't want this to change my back end data, but it is.
Thanks very much.

It is doing the right thing. If you take a core data object and modify it, it will reflect immediately whether you save it ot not. The saving part ensures, that if you quit the application and come back the data is saved as well.
So for your situation, I would avoid modifying the actual core data object. Rather create a structure which imitates the core data object and modify that structure.
Example, say my coredata object is Person with attributes name and age.
The object a get from a fetch is say
person1.
You have a class PersonSub with same attributes.
Now you can create
PersonSub *personSub = [[PersonSub alloc] init]; //You can create a custom init to initilize from Person core data if you like.
personSub.name = person1.name;
personSub.age = person1.age;
Now you can modify as follows
personSub.age = personSub.age + 1;

Related

Indexing and Sorting a UITableView with Values Parsed from an XML by Date

I am parsing an XML file from a URL, from this XML I parse titles of events, their dates, and locations of the events among other things. These elements are cached in an array called stories which acts as my data source for my table view. My goal is to display and indexed UITableView with the sections based on the date at which my events take place by month (January's events in a section, February's events in another, with month names as the titles for my sections). The events that will take place soonest will be at the top of my view, events further out should be at the bottom of my list, I don't have to worry about events that have already taken place. So far I've been able to display my event names and their dates as subtitles in my UITableView not sorted or indexed, left in the order by which they were parsed. When I log the stories array I view this in the console:
2013-01-04 00:17:22.332 NumberTwo[72213:c07] the stories array is as follows: (
{
additionalDesc = "";
allday = "Yes.";
endtime = "11:55 PM";
isodate = "2013-05-23";
location = "";
starttime = "12:00 AM";
title = "CST Exam Makeups";
},
{
additionalDesc = "";
allday = "No.";
endtime = "12:00 PM";
isodate = "2013-06-01";
location = "";
starttime = "8:00 AM";
title = "SAT & Subject Tests";
},)
There is an example of two elements in the array, I understand I am going to have break down the stories array into smaller ones based on the month that the events occur but I don't know what that method would look like. Here's my attempt at it in the viewDidLoad.
- (void)viewDidLoad {
[super viewDidLoad];
self.sections = [NSMutableDictionary dictionary];
for (UIEvent *event in stories)
{
NSDate *dateRepresentingThisDay = tempDate;
// If we don't yet have an array to hold the events for this day, create one
NSMutableArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
if (eventsOnThisDay == nil) {
eventsOnThisDay = [NSMutableArray array];
}
// Add the event to the list for this day
[eventsOnThisDay addObject:event];
}
// Create a sorted list of days
NSArray *unsortedDays = [self.sections allKeys];
self.sortedDays = [unsortedDays sortedArrayUsingSelector:#selector(compare:)];
}
I know the line for (UIEvent *event in stories) must be incorrect but I'm sure what should replace it. tempDate is a formatted date I receive from an NSDateFormatter in another method, and event is meant to refer to an element in my array stories. If you could, point me in the right direction to accomplish my goal, it would be much appreciated. Thank You.
For this you have to use the following steps:
1. On the number of section method of table view write the array count.
2. you also have to modify your array by comparing the dates which are available on stack overflow and arrange in array according to your requirement.
3. Set title by giving title.text = [your array object at index[index path of section]]
4. Now you can give rows as you like in it.
This are just steps you need to follow..
Have you got what I mean to say?
Is it possible for you create a class for the above data and have NSDate property to represent your start date/time of the event? If so, you can make use of NSPredicate to sort the array based on your start date property. You can find details on NSPredicate on this link.

Update original NSMutableArray after filtering with NSPredicate

I have recently started programming for the iOS Platform but now I need some help figuring out how to do 'something':
For my application I fetch some JSON data and put this data as objects into an Array
This Array is written to my own PLIST file (in the docs directory)
Now when the users starts a sync action I:
Fetch the data from the PLIST
Get the timestamp for a certain object in the Array that came from the PLIST
Use timestamp in new JSON request (for the new data)
So far so good.
Now for my (current) problem -> After receiving the new data (JSON req) I wish to update the timestamp of this 'certain' object in the array (and write this to the Plist).
Using an NSPredicate I am able to find the right set of data within the main Array (stampArr).
NSString *documentsDir = [NSHomeDirectory()stringByAppendingPathComponent:#"Documents"];
NSString *plistPath = [documentsDir stringByAppendingPathComponent:#"stamps.plist"];
NSMutableArray *stampArr = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"eventid = 1"];
NSMutableArray *filteredStampArr = [stampArr filteredArrayUsingPredicate:filter];
But now, after I update the filteredStampArr, I want to update the main Array with the data from the filtered Array.
In other words, I need to update the object from the Array with the new 'timestamp' (field of object).
I could off course use something like [stampArr addObject: [filteredStampArr copy]] after changing the filterd array but that would just create a duplicate of the information. I wish to overwrite the original object.
Somehow (I think) I need a 'pointer' that tells me the location of the data in the original array so that I can change the data directly in the main array?
(I hope my questions is clear - If not please say so)
Get the item, find it's index in stampArr and replace it with the newItem.
NSArray *filteredStampArr = [stampArr filteredArrayUsingPredicate:filter];
id item = [filteredStampArr objectAtIndex:0]; // id because the type of the item is not known
NSUInteger itemIndex = [stampArr indexOfObject:item];
[stampArr replaceObjectAtIndex:itemIndex withObject:newItem];
When you get filteredArray, you can directly update objects in it (not replace) and thay willbe uopdated in main array.
Read the API carefully!
try:
[stampArr filterUsingPredicate:];

How to refer to an object in a NSDictionary that is in a NSArray

When I NSLog the array I get this:
(
{
content = "content a";
id = 452069;
timestamp = 1313341470;
},
{
content = "content b";
id = 451498;
timestamp = 1313261505;
},
etc...
)
How do you refer to specific index's? For example how would you get the content for the second index.
I've tried [[myArray objectAtIndex:1]objectForKey:#"content"] but that crashes the program.
Also doing [myArray objectAtIndex:1] crashes the program as well.
According to your edit your array is likely over released. Make sure your array is properly retained, if it uses a property make sure the property is set to copy or retain and if you set it internally be sure to use self.myArray = ...; and not myArray = ...'.
Have you tried -valueForKey:? The item at that index is not an object but a value.

Core data only storing last object of JSON feed

I´m using Core Data as local storage in my app. I´ve set it up properly and made subclasses of NSManagedObject for each entity. However, when I´m trying to insert values into my store, it only inserts the last object from my JSON feed.
res = [JSONHandler requestJSONResponse:jsonString];
shows = [res valueForKeyPath:#"Show.Name"];
NSUInteger showIndex = 0;
for(NSString *showName in shows){
showObject = [NSEntityDescription insertNewObjectForEntityForName:#"Show" inManagedObjectContext:managedObjectContext_];
showObject.name = showName;
showObject.iD = [[res valueForKeyPath:#"Show.Id"]objectAtIndex:showIndex];
showObject.desc = [[res valueForKeyPath:#"Show.Description"]objectAtIndex:showIndex];
showObject.activityType = [[res valueForKeyPath:#"Show.ActivityType"]objectAtIndex:showIndex];
showIndex++;
}
This only stores the last object from my JSON feed. Any idea why?
EDIT: It works fine when I do this:
res = [JSONHandler requestJSONResponse:jsonString];
shows = [res valueForKeyPath:#"Show.Name"];
NSUInteger index = 0;
for(NSString *showName in shows){
show = [NSEntityDescription insertNewObjectForEntityForName:#"Show" inManagedObjectContext:managedObjectContext_];
[show setValue:showName forKey:#"name"];
[show setValue:[[res valueForKeyPath:#"Show.Id"]objectAtIndex:index] forKey:#"iD"];
[show setValue:[[res valueForKeyPath:#"Show.Description"]objectAtIndex:index] forKey:#"desc"];
[show setValue:[[res valueForKeyPath:#"Show.ActivityType"]objectAtIndex:index] forKey:#"activityType"];
index++;
}
It´s basically the same thing, isn´t it? But I want to use subclasses of NSManagedObject instead of doing like I did above. Because in the snippet above show is NSManagedObject *show instead of what it should be: Show *show.
How many shows are there? You can find this by doing: NSLog(#"Number of shows: %d.", shows.count);, assuming that shows is an NSArray. It could be that your Core Data code is fine and the JSON parsing itself is at fault.
EDIT: Also, are you correctly saving the changes to the persistent store?
Usually when you see just one of several objects being saved like this, the problem is that a relationship that should be to-many is improperly set as to-one. No matter how many objects you try to add to the relationship, only the last one is set because the relationship can hold only one value.
I think in this circumstance the problem is most likely in the code of the custom subclass instead of the data model itself given that the data model works with generic NSManagedObjects.

Core Data: Save failing on mandatory field, but value should be set!

I have an abstract entity called Block which contains two attributes: column and order (which are not optional), and one relationship, thing, where it is the detail of a 1->M. I have another entity, Whatever, that has Block as its parent, and adds one attribute, someNumber.
My code looks like this:
Whatever *block = (Whatever *)[NSEntityDescription insertNewObjectForEntityForName:#"Whatever" inManagedObjectContext:managedObjectContext];
block.order = 0;
block.column = 0;
block.thing = self.thing;
When I try to save, I get this error:
Failed to save to data store: Operation could not be completed. (Cocoa error 1560.)
DetailedError: {
NSLocalizedDescription = "Operation could not be completed. (Cocoa error 1570.)";
NSValidationErrorKey = column;
NSValidationErrorObject = <Whatever: 0x5124890> (entity: someWhatever; id: 0x511b4e0 <x-coredata:///Whatever/t718B63A4-927B-4D88-A9E6-7F61CF9621675> ;
data: {
column = nil;
thing = 0x54367a0 <x-coredata://E6648244-E5FC-4202-B5F9-C7A91BACF8DA/Thing/p2>;
order = nil;
someNumber = 0;
});
I don't understand why it says that column and order are nil, as I've just set them the line before, so this shouldn't be a problem.
I've tried using the [block setColumn:0] style as well, without success.
Any help would be appreciated. Thanks!
You are setting them to nil since nil is just a null or zero pointer value.
Core Data properties must be set to objects (as opposed to primitive types).
Integers and floating point numbers are NSNumber objects.
I like to use the numberWith* convenience constructors.
For example:
block.order = [NSNumber numberWithInteger:0];
block.column = [NSNumber numberWithInteger:0];
To expand on gerry3's answer, a great way to ease coding with Core Data is to use Rentzsch's mogenerator. It would allow you to do:
block.orderValue = 0;
block.columnValue = 0;