11

I'm pretty new to CoreData and this app that uses it. And I'm currently working on a feature that clears the entire core data when I log out in the app.

I have 2 sqllite files (for some reason they thought that was handy)

How can I clear both files of all data and reset them into a dataless state?

I've tried a lot of ways, following guides, on SO.

How to clear/reset all CoreData in one-to-many relationship

how to remove all objects from Core Data

They all seem to fail for me. Now I'm wondering what do I do wrong? And perhaps someone can explain me how to reset my 2 CoreData files the proper way.

EDIT:

//should clear the whole coredata database. mainly used for logout mechanism
-(void)resetCoreData
{
    for (NSPersistentStore *store in self.persistentStoreCoordinator.persistentStores)
    {
//    NSPersistentStore *store = self.persistentStoreCoordinator.persistentStores[0];
        NSError *error;
        NSURL *storeURL = store.URL;
        DLog(@"storeURL: %@", storeURL);
        NSPersistentStoreCoordinator *storeCoordinator = self.persistentStoreCoordinator;
        [storeCoordinator removePersistentStore:store error:&error];
        [[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];

        DLog(@"There are erreurs: %@", error);
//    [self addDefaultData];
    }

    _persistentStoreCoordinator = nil;
    _managedObjectContext = nil;
    _managedObjectModel = nil;
}

This doesn't seem to clear the CoreData for me.

EDIT2:

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

- (NSManagedObjectModel *)managedObjectModel
{
    if (__managedObjectModel != nil) {
        return __managedObjectModel;
    }

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyName" withExtension:@"momd"];
    __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    return __managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (__persistentStoreCoordinator != nil) {
        return __persistentStoreCoordinator;
    }

    NSString *storePath = [[self applicationDocumentsDirectory]

                           stringByAppendingPathComponent:@"MyName.sqlite"];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    // If the expected store doesn't exist, copy the default store.
    if (![fileManager fileExistsAtPath:storePath]) {
        NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"MyName" ofType:@"momd"];
        if (defaultStorePath) {

            [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];

        }

    }



    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

    //Check to see what version of the current model we're in. If it's >= 2.0,
    //then and ONLY then check if migration has been performed...
    NSSet *versionIdentifiers = [[self managedObjectModel] versionIdentifiers];
    DLog(@"Which Current Version is our .xcdatamodeld file set to? %@", versionIdentifiers);

    if ([versionIdentifiers containsObject:@"2.0"])
    {
        BOOL hasMigrated = YES;
        if (hasMigrated==YES) {
            storePath = nil;
            storePath = [[self applicationDocumentsDirectory]
                         stringByAppendingPathComponent:@"MyName2.sqlite"];

        }
    }

    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
    NSError *error;
    NSDictionary *pscOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                [NSNumber numberWithBool:NO], NSInferMappingModelAutomaticallyOption,
                                nil];

    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                    configuration:nil
                                                              URL:storeUrl
                                                          options:pscOptions
                                                            error:&error]) {
        DLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return __persistentStoreCoordinator;
}

Edit 3: I'm still looking for a way to reset my CoreData as if I deleted the entire app and started it up again. The usual ways of doing so are not working for my case. And there are 2 sqllite files. There are hints of a migration that took place at some point in the application but I'm not too sure when and how. Error logs show nothing useful.

I'm not looking for the most efficient way. Just the way.

Help me out and the bounty is yours.

Edit 4: FINAL RESULT: It seemed my legacy code had a second ManagedObjectContext instantiated. The moment I retrieved it and did the flush-function with it. Both sqlite-files disappeared as was needed.

Thanks to all that put effort in my problem.

Community
  • 1
  • 1
Totumus Maximus
  • 7,543
  • 6
  • 45
  • 69
  • First thing I can see is that you're setting all you coredata stuff to nil inside the loop. That needs to be don after the loop has finished running. – Fogmeister Feb 06 '13 at 12:53
  • Doesn't work either way. What I notice is that only one of the 2 stores is being logged. What could be a reason for the second one not to be inside the for-loop? – Totumus Maximus Feb 06 '13 at 13:05
  • When you set up the core data stuff it should only have one store anyway. That one store will hold all your data (unless you're doing something that specifically requires more than one)? – Fogmeister Feb 06 '13 at 13:07
  • It's all legacy code. And it's all a mess. And I just started digging in this app. For some reason there are 2 CoreData SQLLite files and there is a hint of a migration function there. But I don't see a need for 2 stores either. – Totumus Maximus Feb 06 '13 at 13:09
  • Ah I see. Do you have the code that sets up and initialises the coredata stack? I can maybe try to decipher what's going on. – Fogmeister Feb 06 '13 at 13:10
  • Since I'm new to this CoreData stuff, I'm not sure where to look into first. And the App is a total mess, it doesn't have a controller layer but instead does everything mainly where ever possible, views, models, data layer, whatever. So I'm not sure what to look for. I see the init of the store and the managedObject at least – Totumus Maximus Feb 06 '13 at 13:23
  • Usually you would have a method like - (NSManagedObjectContext*)managedObjectContext... in the AppDelegate in there it would set up the coredata context. If that isn't there then find where ManageObjectContext is created and set up. – Fogmeister Feb 06 '13 at 13:25
  • I posted both that and the store coordinator – Totumus Maximus Feb 06 '13 at 13:39
  • Also why are those coredata calls always in the app delegate? isn't it much cleaner to make a dedicated class for handling coredata for your app and let him create the managedObjectContext and the likes? – Totumus Maximus Feb 06 '13 at 14:00
  • Yes, you're right. I guess I do it just because that's where it is set as default on a CoreData project. Would be much better to put it into a separate singleton class. – Fogmeister Feb 06 '13 at 14:07
  • My thoughts exactly <3. That's probably the first thing I change when I get the chance. Create me some Singleton controllers and transfer the coredata stuff / the server calls / and the misc data managing stuff to them asap. Atm it is just randomly placed where ever they may be used for that time at the time it was needed. A very bad way to construct an app imho. And I'm rather disappointed I have to deal with this cleaning up shit, instead of making myself useful........ – Totumus Maximus Feb 06 '13 at 14:13
  • In your first listing you make a couple of different calls that take `&error` parameters and you don't check either one of them. Take a look, see what (if anything) those calls are trying to tell you about what went wrong. – Tom Harrington Feb 06 '13 at 17:42
  • I'm sorry to have you confused there. I logged my error-objects for their content, just not in the piece of code I posted. Let me update it and tell you the error object is a nil value – Totumus Maximus Feb 07 '13 at 08:50

4 Answers4

38

try the following method to flush the database, it works perfect for me.

-(void) flushDatabase{
    [__managedObjectContext lock];
    NSArray *stores = [__persistentStoreCoordinator persistentStores];
    for(NSPersistentStore *store in stores) {
       [__persistentStoreCoordinator removePersistentStore:store error:nil];
       [[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:nil];
    }
    [__managedObjectContext unlock];
    __managedObjectModel    = nil;
    __managedObjectContext  = nil;
    __persistentStoreCoordinator = nil;
}
dandan78
  • 13,328
  • 13
  • 64
  • 78
yunas
  • 4,143
  • 1
  • 32
  • 38
  • It's pretty much the same as other answers given. Unfortunately they do not work for me. – Totumus Maximus Feb 15 '13 at 08:21
  • do one thing, apply a break point at the flushdatabase method, and go to your "/Library/Application\ Support/iPhone\ Simulator" and open the database using SQLITEMANAGER or LITA and see the database entries, and when the flush database method is executed refresh the database, you will see that your database is completely flushed. please update me when you do this debugging. – yunas Feb 15 '13 at 09:37
  • I'm doing this debugging now. I have 2 sqlite files. One is nearly empty "db2.sqlite". The other has the data that seems well familiar to me from the app "db.sqlite". Neither of them gets his data deleted after the flush method. – Totumus Maximus Feb 15 '13 at 11:31
  • 1
    I doubt you don't have the right managedobjectcontext with you, when u call the flushdatabase method. – yunas Feb 15 '13 at 13:53
  • Alright! Good news. You were right. Apparantly there was a second object of MOC that was declared somewhere. The moment I accessed and inserted that object into the flush I saw the 2 dbfiles being removed from my finder and saw the results on my screen. Again, hurray for shitty legacy code...... Thanks for your help, this helped me out a lot. – Totumus Maximus Feb 15 '13 at 15:10
2
NSPersistentStoreCoordinator *storeCoordinator = self.persistentStoreCoordinator;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];

So you're removing the persistent store from the persistent store coordinator, and then trying to delete the file. But just removing the store from the coordinator isn't enough to guarantee that the file associated with the store has been closed, and if it hasn't then the file system will probably prevent you from deleting the file. I can't tell from your question whether you're using ARC here, but since you say that it's old code there's a good chance that you aren't. Either way, if removing the store causes it to be autoreleased instead of released, or if you keep any other references to the store anywhere, then the store won't be deallocated just because you removed it from the coordinator, the file may remain open, and deleting the file will fail.

A good way to see what's really going on, in addition to looking at the error object provided by -removeItemAtPath:error:, is to take a look in the file system and see whether the file is still there after you try to remove it. Go to the Organizer window, select your device and application, and download a copy of the app's sandbox. You should then be able to see whether the file is really being deleted.

If you do see that the files are being deleted as you think they should be, then look for other ways that the data may be restored. If UserA logs out and then UserB logs into the app while it's still running, are you sure that you've started with a fresh managed object context? Is it possible that UserA's data remains in the MOC? It could then be written out when you create and add a new store. Another possibility that occurs to me is that iCloud is "helping" here. I see that your data files are kept in the Documents directory, and iOS will normally try to keep files in that directory backed up to iCloud. Perhaps when you create a new store for UserB's data, iCloud adds UserA's records back to that store. (Seems unlikely -- I'm pretty sure iCloud is more sophisticated than that -- but something to check.)

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • Ok let me clear things out a bit. 1. ARC is used in the app, release functions and autorelease functions are never used. When I "logout" I'm not detecting any change in the file system whatsoever. iCloud isn't involved either. The MOC is a good shot tho. I'm not certain how that is managed within the app. Can you tell me how I should manage it? – Totumus Maximus Feb 15 '13 at 09:19
1

I use this function in the AppDelegate of one of my apps...

- (void)deleteAllCoreData
{
    NSPersistentStore *store = self.persistentStoreCoordinator.persistentStores[0];
    NSError *error;
    NSURL *storeURL = store.URL;
    NSPersistentStoreCoordinator *storeCoordinator = self.persistentStoreCoordinator;
    [storeCoordinator removePersistentStore:store error:&error];
    [[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];

    __persistentStoreCoordinator = nil;
    __managedObjectContext = nil;
    __managedObjectModel = nil;

    [self addDefaultData];
}

It deletes the persistent store that CoreData uses and leaves it so that a new one is set up when core data is accessed again.

This is the method described in the second link you provided.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • Yeah, well sort of. The problem however is that it is not working for me. – Totumus Maximus Feb 06 '13 at 11:32
  • What isn't working? What errors are you getting? How do you know it isn't working. – Fogmeister Feb 06 '13 at 11:32
  • I get no errors whatsoever. But I notice when I do logout and log back in under another account that all the data stored in the CoreData of the previous account is still there and visible. – Totumus Maximus Feb 06 '13 at 11:37
  • Are you adding the data back in when the app starts? – Fogmeister Feb 06 '13 at 11:38
  • The only way of adding data to the CoreData is by making a call to a server. But I don't make any servercalls for test purposes atm so the data should be totally empty. – Totumus Maximus Feb 06 '13 at 11:40
  • Not sure then. If you're testing, are you sure you're using a fresh object for testing? This works perfectly for me. Takes 50,000 plus records in various tables and removes it all in a fraction of a second. – Fogmeister Feb 06 '13 at 11:42
  • 1
    Without seeing any code I can't help any further. The code I posted works as I'm using it to do exactly this. If something else in your code isn't set up right then it may cause a problem. – Fogmeister Feb 06 '13 at 12:50
  • de code is posted in my question now. Perhaps you can find out what it is? Maybe a migration thing? – Totumus Maximus Feb 08 '13 at 08:08
  • Your sample code only looks for errors from `removeItemAtPath:error:`. What about the call to `removePersistentStore:error:`? Your code ignores the return value from that call and the error parameter. I bet that's failing, and it would be good to know why. – Tom Harrington Feb 15 '13 at 00:25
1

If your app can create the files then just remove them when the app quits. If you have some notion of a database that has valuable schema info, and you just need to truncate the file, that isn't really the case in core data... The schema is in the compiled entity models and xcwhatever files.

Also I must have just read it wrong, because if you want core data to be not persistent then you are using the wrong tool.

Grady Player
  • 14,399
  • 2
  • 48
  • 76
  • I don't agree with your last statement. Core Data has much more advantages than persistency. It's normal to have part of the model persistent and another part transient using `NSInMemoryStoreType`. That's why it is there, right? – Tricertops Feb 07 '13 at 07:18
  • Ofc i want my data to be persistent. Just want to clear it all when you log out with your account. Which is a rare case but not one that i should ignore. A user is automatically logged in after he registered or logged in at some point. And can only log out manually. Which is a conscience act of the user to clear the local data of that particular user. So i need to be prepped for a new (or the same) user when he logs back in. – Totumus Maximus Feb 07 '13 at 08:07
  • @iMartin, I guess you could use it without a persistent persistent store... for the undo management maybe... But it certainly is a less compelling use case. – Grady Player Feb 08 '13 at 04:44
  • @GradyPlayer There is more than undo management: inverse relationships, KVO, versioning, … (in future maybe working iCloud sync). I usually have few entities persistent and the rest is in in-memory store. Just few clicks to persist them (if I decide so) without need to modify anything. – Tricertops Feb 10 '13 at 08:13
  • I am not a core data detractor, but everything else you get from core data is pretty trivial to impliment, and comes with a hefty toll of having to either run everything on a single thread, or perform locking, or the crazy merge notification updating. – Grady Player Feb 10 '13 at 15:24