Clean (remove) a database in MagicalRecord
Asked Answered
K

6

15

I have an app that is using MagicalRecord for its Core Data handling and this works nice. However I have different users that can login in the app and when another user logs in, the core data database must be emptied so that the different user can have his own data. The database can be emptied completely as the data is also stored on a webservice and therefore can always be synced again after logging in again the first user.

So far I cannot seem to find a helper method (that works) for this purpose. I have tried

[MagicalRecord cleanUp];

whenever the user is logging out, but this does not do the trick.

Kinaesthesia answered 28/12, 2012 at 12:26 Comment(1)
I don't know if Magical Record has something to manage this. You need to wait the author for this. Anyway, you could simply bypass it removing the db file created and newly initializing the stack through MagicalRecord. Maybe, you could open a pull request (GitHub account required) in MagicalRecord project.Footnote
T
18

MagicalRecord does not provide this functionality for you. The cleanUp method is provided for you to reinitialize your CoreData stack in memory and cleaning up any contexts, queues and other related objects. However, it is not that difficult to do yourself given that MagicalRecord does provide a handy method to get the path for your library.

Check out the -[NSPersistentStore MR_urlForStoreName:] method. This will give you the file url for your store. You can then delete it with an NSFileManager instance. Be careful to do this before you set up the Core Data stack or you'll crash when you save because you'd have yanked out the store from under a properly initialized stack.

Thomas answered 28/12, 2012 at 16:58 Comment(4)
if you see my comment I suggested the same thing ;-) +1 for your deeper explanationFootnote
Your suggestion for a pull request or an issue on the repo is also welcome :)Thomas
I just inserted a new issue on GitHub. Cheers.Footnote
Was this ever added to MR?Chamois
C
38

This is how I did it. It is essential to have this line: [MagicalRecord cleanup]. Without it, [self setupDB] won't work.

UPDATE: Deletes the -wal and -shm files. @thattyson pointed out an issue in iOS 9. Also, see the answer of @onmyway133.

- (void)setupDB
{
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:[self dbStore]];
}

- (NSString *)dbStore
{
    NSString *bundleID = (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey];
    return [NSString stringWithFormat:@"%@.sqlite", bundleID];
}

- (void)cleanAndResetupDB
{
    NSString *dbStore = [self dbStore];

    NSError *error1 = nil;
    NSError *error2 = nil;
    NSError *error3 = nil;

    NSURL *storeURL = [NSPersistentStore MR_urlForStoreName:dbStore];
    NSURL *walURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-wal"];
    NSURL *shmURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-shm"];

    [MagicalRecord cleanUp];

    if([[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error1] && [[NSFileManager defaultManager] removeItemAtURL:walURL error:&error2] && [[NSFileManager defaultManager] removeItemAtURL:shmURL error:&error3]){
        [self setupDB];
    }
    else{
        NSLog(@"An error has occurred while deleting %@", dbStore);
        NSLog(@"Error1 description: %@", error1.description);
        NSLog(@"Error2 description: %@", error2.description);
        NSLog(@"Error3 description: %@", error3.description);
    }
}

Here's the Swift version:

func setupDB() {
    MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(self.dbStore())
}

func dbStore() -> String {
    return "\(self.bundleID()).sqlite"
}

func bundleID() -> String {
    return NSBundle.mainBundle().bundleIdentifier!
}

func cleanAndResetupDB() {
    let dbStore = self.dbStore()

    let url = NSPersistentStore.MR_urlForStoreName(dbStore)
    let walURL = url.URLByDeletingPathExtension?.URLByAppendingPathExtension("sqlite-wal")
    let shmURL = url.URLByDeletingPathExtension?.URLByAppendingPathExtension("sqlite-shm")

    var removeError: NSError?

    MagicalRecord.cleanUp()

    //Swift 1
    //let deleteSuccess = NSFileManager.defaultManager().removeItemAtURL(url, error: &removeError)

    //Swift 2
    let deleteSuccess: Bool
    do {
        try NSFileManager.defaultManager().removeItemAtURL(url)
        try NSFileManager.defaultManager().removeItemAtURL(walURL!)
        try NSFileManager.defaultManager().removeItemAtURL(shmURL!)
        deleteSuccess = true
    } catch let error as NSError {
        removeError = error
        deleteSuccess = false
    }

    if deleteSuccess {
        self.setupDB()
    } else {
        println("An error has occured while deleting \(dbStore)")
        println("Error description: \(removeError?.description)")
    }
}
Coruscate answered 2/8, 2013 at 9:24 Comment(8)
but what about the -shm and -wal-files? Those aren't deleted this way, right?Pharisaism
Not working for me. I receive the following error: "The operation couldn’t be completed. No such file or directory"Enthrone
As of iOS9 if you don't delete -shm and -wal files your app will crash with the error SQLite error code:522 after trying to setup your DB a second time.Erinnerinna
@thattyson, did you mean if your base SDK is iOS 9?Coruscate
@Coruscate Sorry yes, if your base SDK is iOS 9 - deletion and set up will work but you'll encounter errors and crashes when attempting to access the new DB. Deletion of -wal and -shm before self.setupDB() fix this.Erinnerinna
@thattyson, it didn't crash for me but it gives unexpected results--some data are not saved (properly). But, yes, deleting the -wal and -shm files becomes necessary.Coruscate
I am getting this error Error1 description: Error Domain=NSCocoaErrorDomain Code=4 "“ShoppingList” couldn’t be removed."Procora
@Wongzigii, what were you doing--what's the code--when you got that error?Coruscate
M
20

To expand on @yoninja 's answer, this will make reset CoreData stack explicitly, plus dealing with wal and shm files

- (void)setupDB
{
    [MagicalRecord setDefaultModelNamed:@"Model.momd"];
    [MagicalRecord setupCoreDataStack];
}

- (void)cleanAndResetupDB
{
    [MagicalRecord cleanUp];

    NSString *dbStore = [MagicalRecord defaultStoreName];

    NSURL *storeURL = [NSPersistentStore MR_urlForStoreName:dbStore];
    NSURL *walURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-wal"];
    NSURL *shmURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-shm"];

    NSError *error = nil;
    BOOL result = YES;

    for (NSURL *url in @[storeURL, walURL, shmURL]) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:url.path]) {
            result = [[NSFileManager defaultManager] removeItemAtURL:url error:&error];
        }
    }

    if (result) {
        [self setupDB];
    } else {
        NSLog(@"An error has occurred while deleting %@ error %@", dbStore, error);
    }
}
Mehalek answered 9/8, 2014 at 15:56 Comment(2)
This works for me too, but it seems like sometimes private contexts on background threads are left dangling after the logout. Any idea how I can fix that? Appreciate your help.Eduardo
Why are these not class functions?Pennyroyal
T
18

MagicalRecord does not provide this functionality for you. The cleanUp method is provided for you to reinitialize your CoreData stack in memory and cleaning up any contexts, queues and other related objects. However, it is not that difficult to do yourself given that MagicalRecord does provide a handy method to get the path for your library.

Check out the -[NSPersistentStore MR_urlForStoreName:] method. This will give you the file url for your store. You can then delete it with an NSFileManager instance. Be careful to do this before you set up the Core Data stack or you'll crash when you save because you'd have yanked out the store from under a properly initialized stack.

Thomas answered 28/12, 2012 at 16:58 Comment(4)
if you see my comment I suggested the same thing ;-) +1 for your deeper explanationFootnote
Your suggestion for a pull request or an issue on the repo is also welcome :)Thomas
I just inserted a new issue on GitHub. Cheers.Footnote
Was this ever added to MR?Chamois
K
5

The following will completely delete the MagicalRecord CoreData sqlite files, as well as the -wal and -shm files. MagicalRecord puts them all in the Library folder; this will simply remove all files from the folder. This will not work if you have other data you need to persist in the Library folder, I did not:

- (void)resetCoreDataDB
{
    [MagicalRecord cleanUp];
    [self deleteFilesInLibrary];
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"YourDBName.sqlite"];
}

- (void)deleteFilesInLibraryDirectory
{
    NSString* folderPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSError *error = nil;
    for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:&error])
    {
        [[NSFileManager defaultManager] removeItemAtPath:[folderPath stringByAppendingPathComponent:file] error:&error];
        if(error)
        {
            NSLog(@"Delete error: %@", error.description);
        }
    }
}
Kalynkam answered 29/7, 2014 at 13:10 Comment(0)
C
2

If you are using the iOS Simulator and deleted the database file, you may probably notice that the data is still there. However, if tested on an actual device (which should be), the file is deleted and the context is reset as should be.

[MagicalRecord cleanUp];

// delete database file
NSError *error;
NSURL *fileURL = [NSPersistentStore MR_urlForStoreName:@"db.sqlite"];
[[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
if(error) {
    // Hanldle error
}

// reset setup.
[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"db.sqlite"];
Ctn answered 10/1, 2014 at 1:26 Comment(0)
S
0

A bit rewritten answer of @yoninja for Swift 4:

private var dbStore : String? {
    get {
        if let bundleId = Bundle.main.bundleIdentifier {
            return bundleId + ".sqlite"
        }
        return MagicalRecord.defaultStoreName()
    }
}

func setupCoreDataStack() {
    MagicalRecord.setupCoreDataStack(withAutoMigratingSqliteStoreNamed: self.dbStore)
}

func cleanUp() {        
    MagicalRecord.cleanUp()

    var removeError: NSError?
    let deleteSuccess: Bool
    do {
        guard let url = NSPersistentStore.mr_url(forStoreName: self.dbStore) else {
            return
        }
        let walUrl = url.deletingPathExtension().appendingPathExtension("sqlite-wal")
        let shmUrl = url.deletingPathExtension().appendingPathExtension("sqlite-shm")

        try FileManager.default.removeItem(at: url)
        try FileManager.default.removeItem(at: walUrl)
        try FileManager.default.removeItem(at: shmUrl)

        deleteSuccess = true
    } catch let error as NSError {
        removeError = error
        deleteSuccess = false
    }

    if deleteSuccess {
        self.setupCoreDataStack()
    } else {
        print("An error has occured while deleting \(self.dbStore)")
        print("Error description: \(removeError.debugDescription)")
    }
}
Spiderwort answered 18/1, 2018 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.