Syncing XML with Core Data

Discussion in 'iOS Programming' started by sleaver, Jul 26, 2010.

  1. macrumors member

    Joined:
    Jul 21, 2010
    #1
    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?
     
  2. macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #2
    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.
     
  3. macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #3
    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.
     
  4. thread starter macrumors member

    Joined:
    Jul 21, 2010
    #4
    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.
     
  5. macrumors 603

    Joined:
    Aug 9, 2009
    #5
    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.


    Post your code.
     
  6. thread starter macrumors member

    Joined:
    Jul 21, 2010
    #6
    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:

    PHP:
        NSMutableArray *items;
        
    NSMutableArray *currentItems;
    This is where I'm hoping to remove objects
    PHP:
    [self currentItems];
        if ([
    currentItems count] > 0) {
            [
    items removeObjectIdenticalTo:currentItems];
        }
    currentItems
    PHP:
    - (void)currentItems {
        
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor allocinitWithKey:@"date" ascending:NO];
        
    NSArray *sortDescriptors = [[NSArray allocinitWithObjects:&sortDescriptor count:1];
        
        
    NSMutableArray *sortedItems = [[NSMutableArray allocinitWithArray:[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.
     
  7. macrumors 603

    Joined:
    Aug 9, 2009
    #7
    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.
     
  8. thread starter macrumors member

    Joined:
    Jul 21, 2010
    #8
    I think I have cracked it also long as someone can confirm what I'm thinking is true.

    My code is now this
    PHP:
    NSManagedObjectContext *context = [feed managedObjectContext];
        
        
        
    NSFetchRequest request = [[NSFetchRequest allocinit];
        
        [
    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?
     
  9. thread starter macrumors member

    Joined:
    Jul 21, 2010
    #9
    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 :)
     
  10. macrumors 603

    Joined:
    Aug 9, 2009
    #10
    Post the actual code that didn't work.

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

    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.


    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.
     
  11. thread starter macrumors member

    Joined:
    Jul 21, 2010
    #11
    The code would have been, as I said the same as before but with removeObjectsInArray but as requested here it is

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

    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?
     

    Attached Files:

  12. macrumors 603

    Joined:
    Aug 9, 2009
    #12
    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).
     
  13. thread starter macrumors member

    Joined:
    Jul 21, 2010
    #13
    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 :(
     

Share This Page