Unable to remove SQLite Database file - ios

I am new to Xcode and ios. I am using Xcode 7.3 version and swift2.2
I have removed a per-populated SQlite Db from my project. Later, I used Add file menu in Xcode to add the same name SQLite Db with only modified the content of the field. Example , fieldname :addr , the content :123, 5th ave , now the same filedname :addr ,the content :12, Broadway
After added the modified content SQLite DB, the code STILL using the old content that is 123,5th ave!
let querySQL = "select Sid, location, Addr from tblPlaces where Sid =" + myId
let result: FMResultSet? = MyDB.executeQuery(querySQL, withArgumentsinArray:nil)
if result?.next() == true {
let strAddr = results!stringForColumn("Addr")
}else {
}
I have created a class to handle the creation of SQLite DB in AppDelegate:
Util.copyFile("SqliteDB filename")
here the code:
class func copyFile(fileName: NSString) {
var dbPath: NSString = getPath(fileName)
var fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(dbPath) {
let documentsURL = NSBundle.mainBundle().resourceURL
let fromPath = documentsURL!.URLByAppendingPathComponent(filename as string)
var error : NSError?
do {
try filemanager.CopyItemAtPath(fromPath.path!, toPath: dbPath)
}catch let error1 as NSError {
error = error1
}
}
// GetPath of the SQLite file
class func getPath(filename : String) -> string {
let documentDirectory = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory,inDomain:
UserDomainMask, appropriateURLURL:nil, create: true)
return documentDirectory.URLbyAppendingPathComponent(filename).path!
}
What this happened? How to do the right way?
If I remove the SQlite Db file say MyDB.sqlite, and later I add in the same name Db file with content modified, I should get the new content. But this is not the case.

You are putting your file in the application bundle, i.e., to be distributed with your app. That file is read only. To update the file (including scheme restructuring) you would need to copy the the file to your documents folder, and update it there. Otherwise, your update will fail, leaving you with the same file as before.
I think this is what is happening.
For distribution it is also necessary that any such file be flagged NOT to backup to icloud or the app is rejected.
I use the following method (sorry, obj c) to copy the file from the bundle to the documents directory (sorry this code is sloppy but it may convey the basic idea).
+ (void)copyBundleFileToStoresDirectory:(NSString *)filename
{
NSError *error;
NSURL *fileURL = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:filename ofType:nil]];
NSURL *pathURL = [SVOFileSystemMethods documentsSubdirectory:#"Stores" skipBackup:YES];
if (fileURL)
{
if([[NSFileManager defaultManager] copyItemAtURL:fileURL toURL:pathURL error:&error])
{
// NSLog(#"File successfully copied");
}
else
{
[[[UIAlertView alloc]initWithTitle:NSLocalizedString(#"error", nil) message: NSLocalizedString(#"Failed to copy database from bundle.", nil)
delegate:nil cancelButtonTitle:NSLocalizedString(#"OK", nil) otherButtonTitles:nil] show];
NSLog(#"Error description-%# \n", [error localizedDescription]);
NSLog(#"Error reason-%#", [error localizedFailureReason]);
}
}
}
// // flags URL to exclude from backup //
+ (BOOL) addSkipBackupAttributeToItemAtURL:(NSURL *)URL {
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success)
{
NSLog(#"Error excluding %# from backup %#", [URL lastPathComponent], error);
}
return success; }

Related

CallDirectory Handler extension error while using Realm

I'm using Callkit extension to identify the numbers. All my contacts (around 30k+) are stored in Realm.
I have stored the Realm file in AppGroup which can be shared between my app and its extensions.
I get the error when I try to reload the extension.
Error Domain=com.apple.CallKit.error.calldirectorymanager Code=7
"(null)"
When this error occurred , the setting for Call Blocking & Identification for my app shows a spinner (while other apps show the switch to toggle)
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[CXCallDirectoryManager sharedInstance] reloadExtensionWithIdentifier:#"com.j2x.handheldcontact.CallerID" completionHandler:^(NSError *error){
if(error) {
NSLog(#"CallerID - refresh failed. error is %#",[error description]);
}
}];
}
I see that the error happens only when I try to use access the Realm in the app group directory.
In my extension subclass:
- (void)beginRequestWithExtensionContext:(CXCallDirectoryExtensionContext *)context
{
context.delegate = self;
NSString *appGroupId = #"group.com.j2x.handheldcontact.CallerID";
NSURL *appGroupDirectoryPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId];
NSURL *dataBaseURL = [appGroupDirectoryPath URLByAppendingPathComponent:#"default.realm"];
[[[RLMRealm defaultRealm]configuration]setFileURL:dataBaseURL];
RLMResults *temp = [self getContactArray]; //This gives the callKit error
RLMResults *temp ; //This doesn't give any error
[context completeRequestWithCompletionHandler:nil];
}
-(RLMResults *)getContactArray{
RLMResults *res = [[RealmContact allObjects]objectsWithPredicate:[NSPredicate predicateWithFormat:#"phone <> nil or homePhone <> nil or mobilePhone <> nil or altPhone <> nil or fax <> nil"]];
return res;
}
Why does accessing the Realm data gives the error ? The predicate format does look ok to me.
With some research I found the following code:
public enum Code : Int {
public typealias _ErrorType = CXErrorCodeCallDirectoryManagerError
case unknown
case noExtensionFound
case loadingInterrupted
case entriesOutOfOrder
case duplicateEntries
case maximumEntriesExceeded
case extensionDisabled
#available(iOS 10.3, *)
case currentlyLoading
#available(iOS 11.0, *)
case unexpectedIncrementalRemoval
}
In my case , the error says case currentlyLoading (code 7). I also tried this on realm with only 250 contacts . But I got the same error.
Edit:
If I hardcode the contact, it works fine. But if I bring Realm into scene, it fails.
CXCallDirectoryPhoneNumber phoneNumber = strtoull([#"14xxxxxx86" UTF8String], NULL, 0);
if (phoneNumber > 0) {
[context addIdentificationEntryWithNextSequentialPhoneNumber:phoneNumber label:#"Test Test"];
}
Workaround:
For now, I'm storing all my data into a file and saving that file in the app group.
NSString *appGroupId = #"group.xxx.CallerID";
NSURL *appGroupDirectoryPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId];
NSURL *appFile = [appGroupDirectoryPath URLByAppendingPathComponent:#"contacts.txt"];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:[appFile path]];
if(exists) {
[[NSFileManager defaultManager]removeItemAtPath:[appFile path] error:nil];
}
[NSKeyedArchiver archiveRootObject:uniqueCallDirectory toFile:[appFile path]];
and accessing this array in the callID extension subclass.
The list you provide to addIdentificationEntryWithNextSequentialPhoneNumber must be ordered by ascending phone Number. Retrieve the list from realm in ascending order. (else it will break and keep the loading icon when you activate the extension)

CoreData detect database inconsistency on start up

We have some code to determine if there is no database file currently created. If this is the case, we do some initialization routines to populate the user's database based on some files that may exist on the file system (basically a migration routine).
The routine is basically like this
NSURL * defaultStorePath = [NSPersistentStore MR_defaultLocalStoreUrl];
BOOL initializeDatabase = ![[NSFileManager defaultManager] fileExistsAtPath:[defaultStorePath path]];
[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreAtURL:defaultStorePath];
if(initializeDatabase)
// ... ingest user files ...
So this works well if the .sqlite file is not present. However if the .sqlite-wal or .sqlite-shm files are missing/corrupt, we can't find a way to detect this scenario.
We would like to do a data integrity check or something in this case.
Without MagicalRecord:
NSURL *storeURL = ...
NSError *error;
NSPersistentStore *persistentStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];
if (persistentStore) {
// further initialization
} else {
switch (error.code) {
case NSFileReadCorruptFileError: {
NSLog(#"database corrupted.");
// delete .sqlite, -wal and -shm
// make another attempt to add persistent store
break;
}
case NSPersistentStoreIncompatibleVersionHashError: {
NSLog(#"database model updated.");
// migrate
break;
}
default: {
NSLog(#"unresolved error %#", error.localizedDescription);
abort();
}
}
}
Here is an example, and I hope it will be helpful.
func configurePersistentStore() {
let nc = NSNotificationCenter.defaultCenter()
nc.addObserver(self, selector:#selector(self.dataBaseWillBeRecreated(_:)), name:kMagicalRecordPSCMismatchWillDeleteStore, object:nil)
MagicalRecord.setLoggingLevel(MagicalRecordLoggingLevel.Warn)
// if sqlite database does not match the model you provided, delete store.
// MagicalRecord.setShouldDeleteStoreOnModelMismatch(true)
MagicalRecord.setupCoreDataStackWithStoreNamed(kPersistentStoreName)
if ESGlobal.sharedInstance().firstRun { // User first run your app after installation.
self.fillDefaultDataToSQLiteDB() // fill data
}
}

Problems converting code to Swift from obj-c [duplicate]

This question already has an answer here:
Cannot convert value of type '()?' to specified type Bool
(1 answer)
Closed 7 years ago.
Im trying to convert to swift code, which sets files property to "not to backup to iCloud"
Original code looks like this
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString {
NSURL* URL= [NSURL fileURLWithPath: filePathString];
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"Error excluding %# from backup %#", [URL lastPathComponent], error);
}
return success;
}
I came with the such code
func addSkipBackupAttributeToItemAtURL(URL: NSURL) -> Bool{
let fileManager = NSFileManager.defaultManager()
assert(fileManager.fileExistsAtPath(URL.absoluteString))
var error:NSError?
let success:Bool = try? URL.setResourceValue(NSNumber(bool: true),forKey: NSURLIsExcludedFromBackupKey)
if !success {
print("Error excluding \(URL.lastPathComponent) from backup \(error)")
} else {
print("File at path \(URL) was succesfully updated")
}
return success
}
but it throws error at line with let success:Bool
Cannot convert value of type '()?' to specified type 'Bool'
How to fix that ?
setResourceValue is not returning a Bool to indicate the success, but throws an error when it fails. So you should wrap it in a try catch block.
̶W̶e̶l̶l̶,̶ ̶t̶h̶e̶ ̶d̶o̶c̶u̶m̶e̶n̶t̶a̶t̶i̶o̶n̶ ̶l̶o̶o̶k̶s̶ ̶o̶u̶t̶d̶a̶t̶e̶d̶, the docs describes error handling in Swift at the bottom, but if you jump it's defenition, you would see that the method does not return anything:
public func setResourceValue(value: AnyObject?, forKey key: String) throws

iOS: How can I create a backup copy of my core data base? And how to export/import that copy?

I want to offer the users of my app the possibility to create a backup of the core data database, especially in case he switches to a new device etc.
How would I do that? Especially how can I reimport that file? I mean let's say he makes a backup copy of the database, then changes a ton of stuff and wants to reset to the previous saved backup copy. How would I do that?
Thx!
Take a look at this sample app, it includes functions for making backups, copying backups to and from iCloud, emailing backups and importing backups from email.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/
BTW it's much safer to use migratePersistentStore API to make/import backups if your are doing so to and from ICloud. Also be aware that the sample app assumes you are not using WAL mode which is the default mode for iOS 7. WAL mode uses multiple files which all need to be backed up or copied.
Here is a link to a video demonstrating the sample Apps backup and restore capabilities.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/sample-apps-explanations/backup-files/
Here are the methods used to create copies for backup. Note that it is possible to open the store with multiple persistentStoreCoordinators so no need to close it down while you make a backup. Restoring it does obviously require the existing store to be removed first. Note that there is little difference between the two methods below except that the source store is opened with or without iCloud options.
/*! Creates a backup of the ICloud store
#return Returns YES of file was migrated or NO if not.
*/
- (bool)backupICloudStore {
FLOG(#"backupICloudStore called");
// Lets use the existing PSC
NSPersistentStoreCoordinator *migrationPSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
// Open the store
id sourceStore = [migrationPSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self icloudStoreURL] options:[self icloudStoreOptions] error:nil];
if (!sourceStore) {
FLOG(#" failed to add old store");
migrationPSC = nil;
return FALSE;
} else {
FLOG(#" Successfully added store to migrate");
NSError *error;
FLOG(#" About to migrate the store...");
id migrationSuccess = [migrationPSC migratePersistentStore:sourceStore toURL:[self backupStoreURL] options:[self localStoreOptions] withType:NSSQLiteStoreType error:&error];
if (migrationSuccess) {
FLOG(#"store successfully backed up");
migrationPSC = nil;
// Now reset the backup preference
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:_makeBackupPreferenceKey];
[[NSUserDefaults standardUserDefaults] synchronize];
return TRUE;
}
else {
FLOG(#"Failed to backup store: %#, %#", error, error.userInfo);
migrationPSC = nil;
return FALSE;
}
}
migrationPSC = nil;
return FALSE;
}
/*! Creates a backup of the Local store
#return Returns YES of file was migrated or NO if not.
*/
- (bool)backupLocalStore {
FLOG(#"backupLocalStore called");
// Lets use the existing PSC
NSPersistentStoreCoordinator *migrationPSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
// Open the store
id sourceStore = [migrationPSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL] options:[self localStoreOptions] error:nil];
if (!sourceStore) {
FLOG(#" failed to add old store");
migrationPSC = nil;
return FALSE;
} else {
FLOG(#" Successfully added store to migrate");
NSError *error;
FLOG(#" About to migrate the store...");
id migrationSuccess = [migrationPSC migratePersistentStore:sourceStore toURL:[self backupStoreURL] options:[self localStoreOptions] withType:NSSQLiteStoreType error:&error];
if (migrationSuccess) {
FLOG(#"store successfully backed up");
migrationPSC = nil;
// Now reset the backup preference
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:_makeBackupPreferenceKey];
[[NSUserDefaults standardUserDefaults] synchronize];
return TRUE;
}
else {
FLOG(#"Failed to backup store: %#, %#", error, error.userInfo);
migrationPSC = nil;
return FALSE;
}
}
migrationPSC = nil;
return FALSE;
}
/** Sets the selected file as the current store.
Creates a backup of the current store first.
#param fileURL The URL for the file to use.
*/
- (BOOL)restoreFile:(NSURL *)fileURL {
FLOG(#" called");
// Check if we are using iCloud
if (_isCloudEnabled) {
FLOG(#" using iCloud store so OK to restore");
NSURL *currentURL = [self storeURL];
FLOG(#" currentURL is %#", currentURL);
FLOG(#" URL to use is %#", fileURL);
[self saveContext];
[self backupCurrentStoreWithNoCheck];
// Close the current store and delete it
_persistentStoreCoordinator = nil;
_managedObjectContext = nil;
[self removeICloudStore];
[self moveStoreFileToICloud:fileURL delete:NO backup:NO];
} else {
FLOG(#" using local store so OK to restore");
NSURL *currentURL = [self storeURL];
FLOG(#" currentURL is %#", currentURL);
FLOG(#" URL to use is %#", fileURL);
[self saveContext];
[self backupCurrentStoreWithNoCheck];
// Close the current store and delete it
_persistentStoreCoordinator = nil;
_managedObjectContext = nil;
NSError *error = nil;
NSFileManager *fm = [[NSFileManager alloc] init];
// Delete the current store file
if ([fm fileExistsAtPath:[currentURL path]]) {
FLOG(#" target file exists");
if (![fm removeItemAtURL:currentURL error:&error]) {
FLOG(#" error unable to remove current store file");
NSLog(#"Error removing item Error: %#, %#", error, error.userInfo);
return FALSE;
} else {
FLOG(#" current store file removed");
}
}
//
//simply copy the file over
BOOL copySuccess = [fm copyItemAtPath:[fileURL path]
toPath:[currentURL path]
error:&error];
if (copySuccess) {
FLOG(#" replaced current store file successfully");
//[self postFileUpdateNotification];
} else {
FLOG(#"Error copying items Error: %#, %#", error, error.userInfo);
return FALSE;
}
}
// Now open the store again
[self openPersistentStore];
return TRUE;
}
Whatever the persistent store is that you use (binary, SQLite, etc.); it is just a file on the filesystem. You can make a copy of it whenever you want.
If you are using SQLite in iOS 7, be sure to make a copy of the other files associated with it as they are the journal files that go with it. If you are using binary then there will be only a single file.
If you just copy the file there is no import step, you just copy it back to restore it.
There are more advanced designs such as exporting the entire database to something that is portable, such as JSON but that is a different subject.
Update
Well I've used the standard Xcode core data template, so according to the code I've just checked I'm using SQLite. So how do I find all related files? Or could you show me with some example code how to copy and insert back the files needed?
You use NSFileManager to copy the files. You can look at the documents directory in your iOS simulator application to see the names of all the files. Or you could use NSFileManager to scan the documents directory, find everything that starts with the same file name (MyData.* for example) and copy that into a back up directory.
As for sample code, no; it is only a couple of lines of code once you look at the documentation for NSFileManager.
I created the following method with the help of Apple sample code. This will take a backup of core data files and place it to the path that you want.
Swift 5
/// Backing up store type to a new and unique location
/// The method is illustrated in the following code fragment, which shows how you can use migratePersistentStore to take a back up of a store and save it from one location to another.
/// If the old store type is XML, the example also converts the store to SQLite.
/// - Parameters:
/// - path: Where you want the backup to be done, please create a new unique directory with timestamp or the guid
/// - completion: Passes error in case of error or pass nil in case of success
class func backUpCoreDataFiles(path : URL, completion : #escaping (_ error : String?) -> ())
{
// Every time new container is a must as migratePersistentStore method will loose the reference to the container on migration
let container = NSPersistentContainer(name : "<YourDataModelName>")
container.loadPersistentStores
{ (storeDescription, error) in
if let error = error
{
fatalError("Failed to load store: \(error)")
}
}
let coordinator = container.persistentStoreCoordinator
let store = coordinator.persistentStores[0]
do
{
try coordinator.migratePersistentStore(store, to : path, options : nil, withType : NSSQLiteStoreType)
completion(nil)
}
catch
{
completion("\(Errors.coredataBackupError)\(error.localizedDescription)")
}
}

Clean (remove) a database in MagicalRecord

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.
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)")
}
}
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);
}
}
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.
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);
}
}
}
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"];
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)")
}
}

Resources