Core Data - How to Insert an Array - iphone

I'd like to know how to save several string objects, all stored in an array, using Core Data.
I understand how to store a single string, but is there any convenience method/can I store the array object itself rather than iterating over the array and storing each string item seperately?
NSManagedObject *alice = [NSEntityDescription insertNewObjectForEntityForName:#"Student" inManagedObjectContext:context];
[alice setValue:#"Alice" forKey:#"name"];
[alice setValue:#"Computer Science" forKey:#"major"];
Basically, can I have setValue be an array instead of Alice?
(As an unrelated question how can I cache data on the iphone, such as an image...)
Thanks for any help

Look at this post : insert NSDictionary into CoreData just replace the word NSDictionary by NSArray apart from that it's the same question.

I don't know if there are any prebuilt methods. You'd have to track keys in a separate container, for example, or define them as constants somewhere, such as in the following example:
static NSUInteger const kMyNameIdx = 0U;
static NSUInteger const kMyMajorIdx = 1U;
static NSString * const kMyNameKey = #"name";
static NSString * const kMyMajorKey = #"major";
/* this does no error checking on the mo or array */
/* being null. it would be better to return an */
/* NSError from this function and check its value */
/* to handle error cases */
- (void) updateManagedObject:(NSManagedObject *)mo withOrderedArray:(NSArray *)array
{
id obj;
NSUInteger objIdx = 0U;
/* this assumes that name and major objects in */
/* your array are in the same order as set by */
/* the constants */
for (obj in array) {
switch (objIdx) {
case kMyNameIdx:
[mo setValue:obj forKey:kMyNameKey];
break;
case kMyMajorIdx:
[mo setValue:obj forKey:kMyMajorKey];
break;
default:
break;
}
objIdx++;
}
}
To use it:
NSManagedObject *alice = [NSEntityDescription insertNewObjectForEntityForName:#"Student" inManagedObjectContext:context];
NSArray *myArray = /* ... */
[self updateManagedObject:alice withOrderedArray:myArray];
There are additional modifications you can make, such as making this a category method for NSManagedObject of entity type Student. Then you can call this function anywhere you use Student managed objects.

where ever you called this method with your array
-(void)viewDidLoad
{
[self insertLoginData:YOUR ARRAY NAME];
}
- (BOOL)insertLoginData:(NSMutableArray *)loginInfoArray
{
NSError *error=nil;
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *propertyInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"UserLogin"
inManagedObjectContext:context];
for(int count=0;count<[loginInfoArray count];count++)
{
[propertyInfo setValue:[[loginInfoArray objectAtIndex:count]objectForKey:#"UserId"] forKey:#"userName"];
[propertyInfo setValue:[[loginInfoArray objectAtIndex:count]objectForKey:#"Password"] forKey:#"password"];
}
if (![__managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
return NO;
}
else
{
return YES;
}
}
Fetch data ==========
-(NSMutableArray *)fetchLoginData
{
NSFetchRequest *fetchReq = [[NSFetchRequest alloc]init];
[fetchReq setEntity:[NSEntityDescription entityForName:#"UserLogin" inManagedObjectContext:self.managedObjectContext]];
NSMutableArray *resultArray = [[NSMutableArray alloc]initWithArray:[self.managedObjectContext executeFetchRequest:fetchReq error:nil]];
NSMutableArray *array=[[NSMutableArray alloc]init];
for(UserLogin *pnt in resultArray)
{
//[array addObject:pnt.userName];
[array addObject:pnt];
}
return array;
}

Related

Optimizing this Core Data request

I have an entity in Core Data named MusicInterest. I have to add 5000 or so of these at a time and my current process is to query to see if the MusicInterest exists already, if not create a new one.
It seems this requires 5000 trips to the store to see if each title exists. There are also, of course, insert trips, but the 5000 queries is what's slowing me down.
Each FacebookFriend will have multiple music interests, and I enumerate through each one using an array of string titles, calling the following code.
Any ideas how to optimize this?
+ (MusicInterest*) musicInterestForFacebookFriend:(FacebookFriend*)facebookFriend WithTitle:(NSString*)musicTitle UsingManagedObjectContext:(NSManagedObjectContext*)moc
{
// query to see if there
NSArray *matches = [self queryForMusicTitle:musicTitle moc:moc];
if (([matches count] >= 1)) {
// NSLog(#"Music already in database");
MusicInterest *existingMusic = [matches lastObject];
[existingMusic addLikedByObject:facebookFriend];
return [matches lastObject];
} else {
// create new Music Interest
MusicInterest *newMusic = [NSEntityDescription insertNewObjectForEntityForName:#"MusicInterest" inManagedObjectContext:moc];
newMusic.title = musicTitle;
[newMusic addLikedByObject:facebookFriend];
return newMusic;
}
}
+ (NSArray *)queryForMusicTitle:(NSString *)MusicTitle moc:(NSManagedObjectContext *)moc
{
// query to see if there
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MusicInterest"];
request.predicate = [NSPredicate predicateWithFormat:#"title == %#", [NSString stringWithFormat:#"%#", MusicTitle]];
NSError *error = nil;
NSArray *matches = [moc executeFetchRequest:request error:&error];
if (error) {
NSLog(#"Error querying title in Music interest. Error = %#", error);
}
return matches;
}
UPDATE:
I employed the design suggested in the Core Data programming guide and it reduced my time from 12 seconds to 4 seconds (still needs some optimization in other areas :)
The guide only includes half the sample code - I thought I would share my complete implementation:
musicArray = [[music componentsSeparatedByString:#", "] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if (obj1 > obj2)
return NSOrderedDescending;
else if (obj1 < obj2)
return NSOrderedAscending;
return NSOrderedSame;
}];
if (musicArray) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MusicInterest"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"title IN %#", musicArray]];
[fetchRequest setSortDescriptors:
#[[[NSSortDescriptor alloc] initWithKey: #"title" ascending:YES]]];
NSError *fetchError = nil;
NSArray *musicInterestMatchingTitles = [backgroundContext executeFetchRequest:fetchRequest error:&fetchError];
if ([musicArray count] > 0) {
// walk musicArray and musicInterestsMatchingTitles in parallel
for (int i = 0; i < [musicArray count]; i++) {
NSString *title = musicArray[i];
if (i < [musicInterestMatchingTitles count]) {
MusicInterest *comparingMusicInterest = musicInterestMatchingTitles[i];
// compare each title
if (![title isEqualToString:comparingMusicInterest.title]) {
// if it doesn't exist as a ManagedObject (a MusicInterest), create one
MusicInterest *musicInterest = [MusicInterest createNewMusicInterestUsingManagedObjectContext:backgroundContext];
musicInterest.title = title;
[musicInterest addLikedByObject:friend];
} else {
// otherwise, just establish the relationship
[comparingMusicInterest addLikedByObject:friend];
}
} else {
// if there are no existing matching managedObjects, create one
MusicInterest *musicInterest = [MusicInterest createNewMusicInterestUsingManagedObjectContext:backgroundContext];
musicInterest.title = title;
[musicInterest addLikedByObject:friend];
}
}
}
}
}];
[self saveBackgroundContext:backgroundContext];
Implementing Find-or-Create Efficiently in the "Core Data Programming Guide" describes a pattern that might be useful here. The basic idea is:
Sort your list of items that you want to insert/update by some unique id that is also stored in
the database.
Perform a single fetch request that fetches all objects from the database that have an id from your list, sorted by the same id.
Now traverse your list and the array of fetched items in parallel, to find which items have to be inserted and which items already exist and can be updated.

Core Data: does a fetch have to make a trip to persistent store?

Say I do this:
NSManagedObjectContext *context = #a managed object context";
NSString *entityName = #an entity name#;
NSFetchRequest *requestForAll = [NSFetchRequest requestWithEntityName:entityName];
NSArray *allObj = [context executeFetchRequest:requestForAll];
for (NSString *name in allNamesArray){
NSFetchRequest *requestForOne = [NSFetchRequest requestWithEntityName:entityName];
requestForOne.predicate = [NSPredicate predicateWithFormat:#"name == %#",name];
NSArray *ObjsWithName = [context executeFetchRequest:requestForOne];
#do some work with the obj#
}
Does the fetch in the loop incur a trip to the persistent store every time? Or those fetches will only be performed in coredata's row cache?
EDIT
I've written a fragment of testing code :
You need to create a core data entity named "Person" and it should have an attribute named "name", which is of type string.
use this code to populate some data:
self.array = #[#"alkjsdfkllaksjdf",#"asldjflkajdklsfjlk;aj",#"aflakjsdl;kfjalksdjfklajkldhkl;aj",#"aljdfkljalksdjfl;j" ,#"flajdl;kfjaklsdjflk;j",#"akldsjfklajdslkf",#"alkdjfkljaklsdjflkaj",#"alsdjflkajsdflj",#"adlkfjlkajsdfkljkla",#"alkdjfklajslkdfj"];
NSString *firstRunKey = #"oh its first run!";
NSString *firstRun = [[NSUserDefaults standardUserDefaults] objectForKey:firstRunKey];
if (!firstRun) {
for (NSString *name in self.array) {
Person *p = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
p.name = name;
}
}
[self.managedObjectContext save];
[[NSUserDefaults standardUserDefaults] setObject:firstRunKey forKey:firstRunKey];
[[NSUserDefaults standardUserDefaults] synchronize];
profile this two methods and you'll find usingCoreData costs much more time than usingFilterArray!
static int caseCount = 1000;
-(void)usingCoreData
{
NSLog(#"core data");
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSArray *allPersons = [self.managedObjectContext executeFetchRequest:request error:nil];
for (int i = 0; i < caseCount; i++){
for (NSString *name in self.array) {
request.predicate = [NSPredicate predicateWithFormat:#"name == %#",name];
NSArray *result = [self.managedObjectContext executeFetchRequest:request error:nil];
}
}
}
-(void)usingFilterArray
{
NSLog(#"filter array");
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSArray *allPersons = [self.managedObjectContext executeFetchRequest:request error:nil];
for (int i = 0; i < caseCount; i++){
for (NSString *name in self.array) {
NSArray *array = [allPersons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name == %#",name]];
}
}
}
Guess I need to answer my question myself.
I tested it and found, every time a fetch executed, core data will translate your NSFetchRequest into SQL command and invoke a data base query,the query result is firstly NSManagedObjectIDs, caching is applied to get the NSManagedObject from a NSManagedObjectID.
In conclusion, it caches object, but doesn't cache query result.
That means you execute the same NSFetchRequest for 10 times, it will query your persistent store for 10 times, event though you will get 10 times the same result. So in such situation, filtering array in memory will perform better than fetching.
The fetch will come from the specified cache when available.
EDIT:
Here's a link to a great tutorial that shows how to set up a NSFetchedResultsController that uses a cache.
http://www.raywenderlich.com/?p=999

Core Data save successful, but no data in database

I've been working on adding Core Data into my iPhone app, and I've been running into some very frustrating issues. When I call save on my context, the save returns successfully, however no data is getting added to my database(I am running this on the simulator and looking at the SQLITE file to check).
I am using the MYDocumentHandler class from this post to use a single UIManagedDocument across multiple classes. I run the code in my AppDelegate as follows:
if (!self.document) {
[[MYDocumentHandler sharedDocumentHandler] performWithDocument:^(UIManagedDocument *document) {
self.document = document;
self.context = document.managedObjectContext;
[self loadView];
}];
}
The loadView method setups up my view controllers once the document has been returned. In my view controllers that use the Core Data I again use something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
if(!self.document){
[[MYDocumentHandler sharedDocumentHandler] performWithDocument:^(UIManagedDocument *document) {
self.document = document;
self.context = document.managedObjectContext;
[self loadAll];
}];
}
}
Where the loadAll method setups everything for the view. When I try to save my data, I use the following:
for (int i = 0; i < [jsonArray count]; i++) {
NSDictionary *dictionary = [jsonArray objectAtIndex:i];
ProjectObject *tempProject = [[ProjectObject alloc] initWithDict:dictionary andETag:etag];
[tempAllProjects addObject:[Projects newProject:tempProject withContext:self.context]];
[tempProject release];
}
[self saveWithContext:self.context];
My saveWithContext method looks like this:
- (BOOL) saveWithContext:(NSManagedObjectContext *)context{
NSError *error = nil;
if (context != nil) {
if ([context hasChanges] && ![context save:&error]) {
DLog(#"Unresolved error %#, %#", error, [error userInfo]);
} else{
DLog(#"save was successful");
return YES;
}
}else{
DLog(#"context is nil");
return NO;
}
}
I always get the save was successful message, and I get the proper messages from the MYDocumentHandler file. Unfortunately, the data is simply not making it to the database. The data is definitely stored in the context, but its not going to the database. Any ideas?
Edit:
Here is the code where I create the Entities:
+ (Projects *) newProject:(ProjectObject *)project withContext:(NSManagedObjectContext *)context
{
Projects *newProject = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Projects"];
request.predicate = [NSPredicate predicateWithFormat:#"project_id = %#", project.project_id];
NSArray *results = [context executeFetchRequest:request error:nil];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([results count] > 0){
newProject = [results objectAtIndex:0];
newProject.account_id = [NSString stringWithFormat:#"%#", [defaults objectForKey:#"account_id"]];
} else {
newProject = [NSEntityDescription insertNewObjectForEntityForName:#"Projects" inManagedObjectContext:context];
}
newProject.account_id = [NSString stringWithFormat:#"%#", [defaults objectForKey:#"account_id"]];
newProject.project_id = project.project_id;
newProject.name = project.name;
newProject.project_description = project.description;
newProject.updated_at = project.updated_at;
newProject.starred = project.starred;
newProject.etag = project.etag;
newProject.synced = [self hasConnection] ? [NSNumber numberWithInt:1] : [NSNumber numberWithInt:0];
return newProject;
}
I found the solution in this post. Not sure if this is the best way to handle it. If there is a better option, please let me know.
You are not saving Manage object context after changing it. I have added the lines into your code.
Please check if it works now.

Error: NSArray was mutated while being enumerated thrown when accessing globals and Core Data

I have this piece of code which I am using to update some values in Core Data when another object is added:
//Create new receipt
Receipt *receipt = [[Receipt alloc] init];
receipt.project = self.projectLabel.text;
receipt.amount = self.amountTextField.text;
receipt.descriptionNote = self.descriptionTextField.text;
receipt.business = self.businessNameTextField.text;
receipt.date = self.dateLabel.text;
receipt.category = self.categoryLabel.text;
receipt.paidBy = self.paidByLabel.text;
receipt.receiptImage1 = self.receiptImage1;
//Need to set this to 2
receipt.receiptImage2 = self.receiptImage1;
receipt.receiptNumber = #"99";
int count = 0;
int catCount = 0;
for (Project *p in appDelegate.projects)
{
if ([p.projectName isEqualToString:receipt.project]){
double tempValue = [p.totalValue doubleValue];
tempValue += [receipt.amount doubleValue];
NSString *newTotalValue = [NSString stringWithFormat:#"%.02f", tempValue];
NSString *newProjectName = p.projectName;
//remove entity from Core Data
NSFetchRequest * allProjects = [[NSFetchRequest alloc] init];
[allProjects setEntity:[NSEntityDescription entityForName:#"Project" inManagedObjectContext:appDelegate.managedObjectContext]];
[allProjects setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * projectsArray = [appDelegate.managedObjectContext executeFetchRequest:allProjects error:&error];
//Delete product from Core Data
[appDelegate.managedObjectContext deleteObject:[projectsArray objectAtIndex:count]];
NSError *saveError = nil;
[appDelegate.managedObjectContext save:&saveError];
[appDelegate.projects removeObjectAtIndex:count];
NSLog(#"Removed project from Core Data");
//Insert a new object of type ProductInfo into Core Data
NSManagedObject *projectInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Project"
inManagedObjectContext:appDelegate.managedObjectContext];
//Set receipt entities values
[projectInfo setValue:newProjectName forKey:#"name"];
[projectInfo setValue:newTotalValue forKey:#"totalValue"];
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Added Project to Core Data");
Project *tempProject = [[Project alloc] init];
tempProject.projectName = [projectInfo valueForKey:#"name"];
tempProject.totalValue = [projectInfo valueForKey:#"totalValue"];
[appDelegate.projects addObject:tempProject];
}
count++;
}
for (Category *c in appDelegate.categories){
if ([c.categoryName isEqualToString:receipt.category]){
double tempValue = [c.totalValue doubleValue];
tempValue += [receipt.amount doubleValue];
NSString *newTotalValue = [NSString stringWithFormat:#"%.02f", tempValue];
NSString *newCategoryName = c.categoryName;
//remove entity from Core Data
NSFetchRequest * allCategories = [[NSFetchRequest alloc] init];
[allCategories setEntity:[NSEntityDescription entityForName:#"Category" inManagedObjectContext:appDelegate.managedObjectContext]];
[allCategories setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * categoriesError = nil;
NSArray * categoriesArray = [appDelegate.managedObjectContext executeFetchRequest:allCategories error:&categoriesError];
//Delete product from Core Data
[appDelegate.managedObjectContext deleteObject:[categoriesArray objectAtIndex:catCount]];
NSError *categorySaveError = nil;
[appDelegate.managedObjectContext save:&categorySaveError];
[appDelegate.categories removeObjectAtIndex:catCount];
NSLog(#"Removed category from Core Data");
NSError * error = nil;
//Insert a new object of type ProductInfo into Core Data
NSManagedObject *categoryInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Category"
inManagedObjectContext:appDelegate.managedObjectContext];
//Set receipt entities values
[categoryInfo setValue:newCategoryName forKey:#"name"];
[categoryInfo setValue:newTotalValue forKey:#"totalValue"];
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Added Category to Core Data");
Category *tempCategory = [[Category alloc] init];
tempCategory.categoryName = [categoryInfo valueForKey:#"name"];
tempCategory.totalValue = [categoryInfo valueForKey:#"totalValue"];
[appDelegate.categories addObject:tempCategory];
}
catCount++;
}
This code gives the error:
'...was mutated while being enumerated'.
Can anyone explain why? Also, is there a better approach to doing what I'm trying to achieve?
The error you're seeing is accurate. The problem is that you're mutating (changing) a collection while iterating over it. Basically, you're doing something of the form:
for (Project *p in appDelegate.projects) {
...
[p addObject: X]
}
This isn't allowed.
One simple solution is to make a new collection of the objects you want to add, and then add them to the original container outside of your loop. Something like:
NSMutableArray *array = [NSMutableArray array];
for (Project *p in appDelegate.projects) {
...
[array addObject:X];
}
[p addObjects:array];
By the way, did you google for the error text "was mutated while being enumerated"? I'd be surprised if you didn't find the answer for this common problem just by googling.
Also, when posting an error message, it's helpful to post the full line, not just part of it.
You are adding and removing items from appDelegate.projects while iterating over it in a for-each loop here:
[appDelegate.projects removeObjectAtIndex:count];
// ...
[appDelegate.projects addObject:tempProject];
And the same for appDelegate.categories:
[appDelegate.categories removeObjectAtIndex:count];
// ...
[appDelegate.categories addObject:tempProject];
AFAIK, in such case you should use a simple for loop, and access the array with index.

Core Data - JSON (TouchJSON) on iPhone

I have the following code which seems to go on indefinitely until the app crashes. It seems to happen with the recursion in the datastructureFromManagedObject method. I suspect that this method:
1) looks at the first managed object and follows any relationship property recursively.
2) examines the object at the other end of the relationship found at point 1 and repeats the process.
Is it possible that if managed object A has a to-many relationship with object B and that relationship is two-way (i.e an inverse to-one relationship to A from B - e.g. one department has many employees but each employee has only one department) that the following code gets stuck in infinite recursion as it follows the to-one relationship from object B back to object A and so on.
If so, can anyone provide a fix for this so that I can get my whole object graph of managed objects converted to JSON.
#import "JSONUtils.h"
#implementation JSONUtils
- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject *)managedObject {
NSDictionary *attributesByName = [[managedObject entity] attributesByName];
NSDictionary *relationshipsByName = [[managedObject entity] relationshipsByName];
//getting the values correspoinding to the attributes collected in attributesByName
NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
//sets the name for the entity being encoded to JSON
[valuesDictionary setObject:[[managedObject entity] name] forKey:#"ManagedObjectName"];
NSLog(#"+++++++++++++++++> before the for loop");
//looks at each relationship for the given managed object
for (NSString *relationshipName in [relationshipsByName allKeys]) {
NSLog(#"The relationship name = %#",relationshipName);
NSRelationshipDescription *description = [relationshipsByName objectForKey:relationshipName];
if (![description isToMany]) {
NSLog(#"The relationship is NOT TO MANY!");
[valuesDictionary setObject:[self dataStructureFromManagedObject:[managedObject valueForKey:relationshipName]] forKey:relationshipName];
continue;
}
NSSet *relationshipObjects = [managedObject valueForKey:relationshipName];
NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];
for (NSManagedObject *relationshipObject in relationshipObjects) {
[relationshipArray addObject:[self dataStructureFromManagedObject:relationshipObject]];
}
[valuesDictionary setObject:relationshipArray forKey:relationshipName];
}
return [valuesDictionary autorelease];
}
- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects {
NSMutableArray *dataArray = [[NSArray alloc] init];
for (NSManagedObject *managedObject in managedObjects) {
[dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
}
return [dataArray autorelease];
}
//method to call for obtaining JSON structure - i.e. public interface to this class
- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects {
NSLog(#"-------------> just before running the recursive method");
NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
NSLog(#"-------------> just before running the serialiser");
NSString *jsonString = [[CJSONSerializer serializer] serializeArray:objectsArray];
return jsonString;
}
- (NSManagedObject*)managedObjectFromStructure:(NSDictionary*)structureDictionary withManagedObjectContext:(NSManagedObjectContext*)moc {
NSString *objectName = [structureDictionary objectForKey:#"ManagedObjectName"];
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:objectName inManagedObjectContext:moc];
[managedObject setValuesForKeysWithDictionary:structureDictionary];
for (NSString *relationshipName in [[[managedObject entity] relationshipsByName] allKeys]) {
NSRelationshipDescription *description = [[[managedObject entity]relationshipsByName] objectForKey:relationshipName];
if (![description isToMany]) {
NSDictionary *childStructureDictionary = [structureDictionary objectForKey:relationshipName];
NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
[managedObject setValue:childObject forKey:relationshipName];
continue;
}
NSMutableSet *relationshipSet = [managedObject mutableSetValueForKey:relationshipName];
NSArray *relationshipArray = [structureDictionary objectForKey:relationshipName];
for (NSDictionary *childStructureDictionary in relationshipArray) {
NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
[relationshipSet addObject:childObject];
}
}
return managedObject;
}
//method to call for obtaining managed objects from JSON structure - i.e. public interface to this class
- (NSArray*)managedObjectsFromJSONStructure:(NSString *)json withManagedObjectContext:(NSManagedObjectContext*)moc {
NSError *error = nil;
NSArray *structureArray = [[CJSONDeserializer deserializer]
deserializeAsArray:[json dataUsingEncoding:NSUTF32BigEndianStringEncoding]
error:&error];
NSAssert2(error == nil, #"Failed to deserialize\n%#\n%#", [error localizedDescription], json);
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
for (NSDictionary *structureDictionary in structureArray) {
[objectArray addObject:[self managedObjectFromStructure:structureDictionary withManagedObjectContext:moc]];
}
return [objectArray autorelease];
}
#end
I answered this question when you posted a comment on the original thread. You need to make some changes to how the recursion works so that it doesn't go into a loop. There are many ways to do this.
For example, you can change the call to get all relationships to instead call a method in your NSManagedObject subclasses that only returns the relationships that are downstream. In that design ObjectA would return the ObjectB relationship but Object B would not return any (or relationships to ObjectC, etc.). This creates a tree like hierarchy for the recursion to work through.
Follow the logic of the code. It process the object or objects you hand to it and then it walks through every object associated with that first set of objects. You already, from your post, showed that you understand it is a loop. Now you need to break that loop in your code with logic to change it from a loop to a tree.
Also, I realize this may sound like I am pimping my book, I explained how to do avoid this loop in my book in the Multi-threading chapter in the section on exporting recipes.
Update NSDate
That sounds like a bug in the JSON parser that you are using as it should be able to handle dates. However your workaround is viable except you need to convert it on both sides which is a PITA. I would look into your parser and see why it is not translating dates correctly as that is a pretty big omission.
I just wanted to point out a small typo, that caused the code to crash, and hopefully this will save you a few min.
- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects {
NSMutableArray *dataArray = [[NSArray alloc] init];
for (NSManagedObject *managedObject in managedObjects) {
[dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
}
return [dataArray autorelease];
}
The NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray
really should be NSMutableArray *dataArray = [[NSMutableArray alloc] init];
that is all.
thank you