PDA

View Full Version : Syncing XML with Core Data




sleaver
Jul 26, 2010, 06:05 AM
As you may have seen from other threads I'm learning iPhone development by writting my first app.

The app uses Core Data to store items from an XML file. What I want to be able to do is read that same XML file at a later date and compare if the items are the same against the one's in core data and if not leave the item if the same, remove if it isn't in the new XML or add if it's a new item. For example the mail application (my app isn't a mail one), after the initial load it will download new emails, leave ones that are still there or delete one's I have deleted via a front end (GMail).

So my question is, is it possible? If so are there any good examples people are aware of?



seepel
Jul 26, 2010, 03:48 PM
Are you familiar with SQL? When I work with Core Data I basically think of it as a front end to SQLite (Which it often is). So basically what you want to do is design a mapping between your input XML file and your Core Data Model. Instances of Core Data Objects are of the NSManagedObject class, so the documentation there is a good place to start. I think as long as your model doesn't get too complicated, it should be relatively straight forward to determine changes between your Core Data Store and the current XML file.

PhoneyDeveloper
Jul 26, 2010, 04:04 PM
What you describe is essentially syncing of two data sets. It's less like Mail and more like syncing of your Contacts database between your device and your Mac. I would look around for some discussion of syncing to get some ideas about how to approach the problem. One-way syncing, two-way syncing etc.

Of course it's possible. You just have to design and write the code.

sleaver
Aug 6, 2010, 03:05 PM
Sorry to bring this up again but I've just got around to implementing it.

I'm trying to keep it simple at the moment so thought I would have two NSMutableArrays, one containing the existing data freshly retrieved from Core Data and the other the new data. Then look for the differences in one field and insert the new objects. However using for loops could end up with a lot of compares, just as an example having 50 items in both could require 2500 checks.

It was then I found removeObjectsInArray. I thought great, one line and it's done. But when I ran through the code I started off with 20 objects in each and ended up with 20 objects when I shouldn't have.

So what am I doing wrong, am I going about it in the best way? Advice along with examples would be gratefully received.

chown33
Aug 6, 2010, 03:49 PM
I'm trying to keep it simple at the moment so thought I would have two NSMutableArrays, one containing the existing data freshly retrieved from Core Data and the other the new data. Then look for the differences in one field and insert the new objects. However using for loops could end up with a lot of compares, just as an example having 50 items in both could require 2500 checks.

Please explain the nature and structure of your data. There may be a faster way of doing what you want, but without knowing the nature of the data, it's impossible to tell.

What you describe sounds like a complete replacement of one dataset with another. Why not treat it like that?

Distribute the data in a way that Core Data can read it directly (XML data store files), and you don't even have to worry about the cost of performing inserts or updates. You simply replace the data-store file(s) and it's ready to go.



It was then I found removeObjectsInArray. I thought great, one line and it's done. But when I ran through the code I started off with 20 objects in each and ended up with 20 objects when I shouldn't have.

So what am I doing wrong, am I going about it in the best way? Advice along with examples would be gratefully received.

Post your code.

sleaver
Aug 6, 2010, 04:11 PM
I'll let you know now that it is an RSS reader so I don't want a complete replacement as I want to maintain a read/not read indicator for a set amount of items. So in my model I have a FeedInfo entity with a relationship to a FeedItem entity. Here though is hopefully the appropriate code that I'm having the problem with:

NSMutableArray *items;
NSMutableArray *currentItems;

This is where I'm hoping to remove objects
[self currentItems];
if ([currentItems count] > 0) {
[items removeObjectIdenticalTo:currentItems];
}

currentItems
- (void)currentItems {
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:&sortDescriptor count:1];

NSMutableArray *sortedItems = [[NSMutableArray alloc] initWithArray:[feed.feeditems allObjects]];
[sortedItems sortUsingDescriptors:sortDescriptors];
self.currentItems = sortedItems;

[sortDescriptor release];
[sortDescriptors release];
[sortedItems release];
}

items is a list of objects that have been read from the RSS feed and I use http://github.com/mwaterfall/MWFeedParser to handle that.

chown33
Aug 6, 2010, 04:37 PM
This is where I'm hoping to remove objects
[self currentItems];
if ([currentItems count] > 0) {
[items removeObjectIdenticalTo:currentItems];
}
...
items is a list of objects that have been read from the RSS feed ...

removeObjectIdenticalTo: does not iterate. If currentItems itself is not in items, then nothing happens.

Read the reference doc; it says removeObjectIdenticalTo: uses indexOfObjectIdenticalTo: to find matches. It says nothing about iterating the contents of the passed object.

You should use removeObjectsInArray:

You're also mixing instance-variable accesses with property accesses, which is generally considered a bad idea, because it can circumvent whatever attributes you give the property, such as atomic or retain.

I'm also wondering why you're not using NSSet, since it offers faster access for keyed access, and it seems unlikely to me that anyone would want two or more instances of the same feed. NSMutableSet also offers set-combining operations: intersect, union, subtraction, etc. In short, it seems to fit your data better than NSArray.

sleaver
Aug 6, 2010, 04:40 PM
I think I have cracked it also long as someone can confirm what I'm thinking is true.

My code is now this
NSManagedObjectContext *context = [feed managedObjectContext];


NSFetchRequest * request = [[NSFetchRequest alloc] init];

[request setEntity:[NSEntityDescription entityForName:@"FeedItem" inManagedObjectContext:context]];
[request setPredicate:[NSPredicate predicateWithFormat:@"title = %@", item.title]];
NSUInteger count = [context countForFetchRequest:request error:nil];
[request release];

if (count == 0) {
// Insert if new
}

Say for example I have two feeds and they just happen to have the same title. Would I run into trouble with the above code or will it work as the NSManagedObjectContext is feed (FeedInfo entity) but I have used the FeedItem entity for the NSEntityDescription?

Lastly if the above is OK do I need to be worried about performing multiple fetches or am I better sticking with it as RSS feeds don't include lots of items?

sleaver
Aug 6, 2010, 04:44 PM
You should use removeObjectsInArray:

I'm also wondering why you're not using NSSet, since it offers faster access for keyed access, and it seems unlikely to me that anyone would want two or more instances of the same feed. NSMutableSet also offers set-combining operations: intersect, union, subtraction, etc. In short, it seems to fit your data better than NSArray.
Sorry, I did use removeObjectsInArray in the same way but that didn't work so I must of pasted the code without changing it back. However it still didn't work but would I still have to itterate through?

Do you know of any good documentation on NSSet or is Apple's reference docs OK?

If you don't mind I would also like your opinion on the post above :)

chown33
Aug 6, 2010, 04:55 PM
Sorry, I did use removeObjectsInArray in the same way but that didn't work so I must of pasted the code without changing it back. However it still didn't work but would I still have to itterate through?

Post the actual code that didn't work.

Read the reference doc of removeObjectsInArray:. If something isn't clear, then ask a specific question.

Do you know of any good documentation on NSSet or is Apple's reference docs OK?

Read Apple's reference docs first. If you don't understand something, ask a specific question. You are the only one who can decide if they are OK for you or not.


If you don't mind I would also like your opinion on the post above :)
Insufficient information. You haven't described the details of your entities.

If you are relying on the uniqueness of titles, that seems foolish. A URL (or URI) is generally considered a unique identifier, not a title.

sleaver
Aug 6, 2010, 05:10 PM
Post the actual code that didn't work.

Read the reference doc of removeObjectsInArray:. If something isn't clear, then ask a specific question.


The code would have been, as I said the same as before but with removeObjectsInArray but as requested here it is

[self currentItems];
if ([currentItems count] > 0) {
[items removeObjectsInArray:currentItems];
}


Insufficient information. You haven't described the details of your entities.

If you are relying on the uniqueness of titles, that seems foolish. A URL (or URI) is generally considered a unique identifier, not a title.
Titles is something I just picked, there is no reason why I can't change this during refactoring. However as requested by entities can be seen in the attached.

IF my NSManagedObjectContext is feed which is a FeedItem object will I have problems if two feeds contain the same or will the NSEntityDescription of FeedItem prevent that? Or by saying using the url as a key for that reason?

chown33
Aug 6, 2010, 05:57 PM
The code would have been, as I said the same as before but with removeObjectsInArray but as requested here it is

[self currentItems];
if ([currentItems count] > 0) {
[items removeObjectsInArray:currentItems];
}


Are the objects in both arrays of the same class?

Does the class implement hash and isEqual:? These methods are specifically mentioned in the docs for removeObjectsInArray:, so it's reasonable to assume there's a reason for it. That reason is likely because removeObject: is used for the actual removal (see reference doc for removeObject).

sleaver
Aug 7, 2010, 05:12 AM
OK, what I thought worked in post #8 is OK for one feed, but after more testing, if two different threads have the same title it won't work as it seems to be check the whole FeedItem entity rather that the items relating to the current FeedInfo.

So, to ask a specific question, does anyone know how I can do this? Days of Googling isn't helping much!

Also please appreciate that I am new to iPhone development so may need pointing in the right direction more than others! Yes I can read the documentation, but if I could work it out from that do you think I would be posting here :(