Some background for this issue, I'm trying to include what I think may be relevant to help understand the context.
I am currently adding an linked library which used Core Data to save some user information and a feature which adds an Entity to the pre-existing Core Data model already in the app. Each managedObjectContext has its own instance when created (verified) as well as its own PSC and MOM and neither interact with the other's entities(thus seem to be independent).
The entirety of the following code, errors, and (I believe issue) is in the Main Target of the app. (Hopefully) not the newly added linked library.
The saveContext method is:
- (void)saveContext {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = nil;
// Register
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myManagedObjectContextDidSaveNotificationHandler:) name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];
if (self.managedObjectContext != nil) {
if ([self.managedObjectContext hasChanges]) {
BOOL success = [self.managedObjectContext save:&error];
if (!success) {
[Error showErrorByAppendingString:NSLocalizedString(#"UnableToSaveChanges", nil) withError:error];
} else {
//
}
}
}
// Unregister
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];
});
}
When called, error = nil, success = NO and by forcing the compiler past the exception I get the following:
CoreData: error: exception during obtainPermenantIDsForObjects: Updating max pk failed: attempt to write a readonly database with userInfo of { NSSQLiteErrorDomain = 1032;
}
I have googled, "NSSQLiteErrorDomain = 1032", "obtainPermenantIDsForObjects", and "CoreData readonly database". It does appear that the key primary key for each object is the same, but I am setting that value, I believe sqlite is. I have not found any solutions to help with this. I do have the argument passed on launch, "Concurrency Debug 1" set to on.
I have not implemented obtainPermenantIDsForObjects and I've searched the whole project and cant find its implementation so I think CoreData is using this.
The saveContext method is called on the main queue because thats how my predecessors rolled out the code and I don't have time at the moment to deal with it.
The method calling saveContext (from a background thread):
- (NSMutableArray *)convertRawStepDataTo:(NSMutableArray*)steps
withDates:(NSMutableArray*)dates
inManagedObjectContext:(NSManagedObjectContext*)theMOC {
NSMutableArray *theStepsArray = [[NSMutableArray alloc] init];
// prepare values for chart
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
StepSelector *theSelector = [[StepSelector alloc] init];
NSString* apiSelectionForStep = [theSelector getCurrentSelectionString];
for (int iter = 0; iter < steps.count; iter++) {
NSNumber *currStepValue = [steps objectAtIndex:iter];
// NSNumber *stepCountforIter = [NSNumber numberWithLong:[[steps objectAtIndex:iter] longValue]];
NSNumber* dateForIter = [NSNumber numberWithLong:[[dates objectAtIndex:iter] longLongValue]];
Step *step = [delegate addStepObjectToPersistentStorewithAPI:apiSelectionForStep
andStep:stepCountforIter
andDate:dateForIter
forMOC:theMOC];
[theStepsArray addObject:step];
if (VERBOSE) {
NSLog(#"This is step number %d, with object ID: %#", count, [theMOC objectWithID:step.objectID]);
count++;
}
}
[delegate saveContext];
return theStepsArray;
}
Thats all I can think that might help. The source for the MOC in the main target is the appDelegate which is where all the core data code was written initially.
EDIT Here is the requested PSC code. The store is located in the documents directory. I discovered that these objects are being saved to the Persistent Store.. but the error is still occurs. Se below for PSC code:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [self getStoreURL];
// Rollback journalling mode...
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption,
NSFileProtectionComplete, NSFileProtectionKey,
#{#"journal_mode": #"TRUNCATE"}, NSSQLitePragmasOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error = nil;
self.persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error];
if (!self.persistentStore) {
NSLog(#"Error: %#",error);
[Error showErrorByAppendingString:NSLocalizedString(#"UnableToFindDatabaseFile", nil) withError:error];
}
return persistentStoreCoordinator;
}
-(NSURL *)getStoreURL {
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kSQLFILENAME];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:SQLFILEPATHRESOURCE ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
return storeUrl;
}
The NSSQLiteErrorDomain key means that this error came from SQLite, and that Core Data is passing it back to you. SQLite defines error 1032 as follows:
The SQLITE_READONLY_DBMOVED error code is an extended error code for SQLITE_READONLY. The SQLITE_READONLY_DBMOVED error code indicates that a database cannot be modified because the database file has been moved since it was opened, and so any attempt to modify the database might result in database corruption if the processes crashes because the rollback journal would not be correctly named.
...which appears to mean that SQLite is making the persistent store file read only because something has happened to it since it was opened, and SQLite is trying to prevent data corruption.
I don't see anything in the code you've posted that is obviously at fault, at least as far as the error code description goes. So I wonder, are you doing anything anywhere else that would directly affect the persistent store file (i.e. touching the file in any way at all instead of going through Core Data fetch/save calls)?
The mention of the rollback journal in the error code description makes me wonder if setting journal_mode to TRUNCATE is related. If it were me, I'd remove that (I don't know what it's intended to accomplish here) or set it to DELETE. At least for testing purposes, anyway, in the hope of understanding the problem better.
I am trying to insert data into coredata but i am getting error like ,
CoreData: error: (1555) UNIQUE constraint failed: ZSIDETABLENAME.Z_PK
Scenario :-
First: from appdelegate.m I am copying data from my SQL file to document directory's sql file .
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
NSString* from;
NSString* to;
NSArray* mainPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [mainPath objectAtIndex:0]; // Get documents folder
/*VoiceRecords.plist file*/
from=[[NSBundle mainBundle]pathForResource:#"News" ofType:#"sqlite"];
to=[documentsDirectory stringByAppendingPathComponent:#"News.sqlite"];
[[NSFileManager defaultManager]copyItemAtPath:from
toPath:to error:nil];
from=nil;
to=nil;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"News.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES}
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
It works correctly. Then i am trying to save url entered by user in coredata in mainviewcontroller.m.
- (void)feedParserDidFinish:(MWFeedParser *)parser {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"IWorld"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"feedurl = %#", self.Feedlink];
NSManagedObjectContext *context = [self managedObjectContext];
if ([[context executeFetchRequest:fetchRequest error:NULL] count] == 0) {
NSError *error = nil;
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"feedurl contains %#",check];
[fetchRequest setPredicate:predicate];
NSMutableArray *checkp = [[context executeFetchRequest:fetchRequest error:&error]mutableCopy];
if (checkp.count<=0) {
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"IWorld" inManagedObjectContext:context];
[newDevice setValue:self.Name forKey:#"name"];
[newDevice setValue:self.WebsiteName forKey:#"websitename"];
[newDevice setValue:self.Feedlink forKey:#"feedurl"];
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
//Save value in lockbox
NSArray *keys = [[[newDevice entity] attributesByName] allKeys];
NSDictionary *dict = [newDevice dictionaryWithValuesForKeys:keys];
}
}
NSFetchRequest *fetchrequestforside=[NSFetchRequest fetchRequestWithEntityName:#"Sidetablename"]; //Sidetablename is entity
fetchrequestforside.predicate=[NSPredicate predicateWithFormat:#"feedurl = %#", self.Feedlink];
NSManagedObjectContext *context = [self managedObjectContext];
if ([[context executeFetchRequest:fetchrequestforside error:NULL] count] == 0) {
// Create a new managed object
NSError *error = nil;
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"feedurl contains %#",check ]; //check is urlstring
[fetchrequestforside setPredicate:predicate];
NSMutableArray *checkp = [[context executeFetchRequest:fetchrequestforside error:&error]mutableCopy];
if (checkp.count<=0) //if coredata will find url then notstore in coredata else stored procedure
{
NSManagedObject *newDevice1 = [NSEntityDescription insertNewObjectForEntityForName:#"Sidetablename" inManagedObjectContext:context];
if (self.Name)
{
[newDevice1 setValue:self.Name forKey:#"name"];
}
if (self.WebsiteName)
{
[newDevice1 setValue:self.WebsiteName forKey:#"websitename"];
}
if (self.Feedlink)
{
[newDevice1 setValue:self.Feedlink forKey:#"feedurl"];
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]); // Getting error here through nslog //CoreData: error: (1555) UNIQUE constraint failed: ZSIDETABLENAME.Z_PK
}
Note: this all happen at first time. Second time when I will run application all work correctly. Don't know where is issue?
You are getting a primary key constraint violation on the sidetablename table.
Thus, I believe your problem is in how you created and are copying your seed database.
Unfortunately, you did not provide that information. Thus, I'll just guess as to how you did it.
I assume you created the database using a mac app or you did it in the iOS simulator. You then copied the sqlite file from wherever it was created into the bundle resources.
Since WAL has been the default mode since iOS/OSX 7/10.9, I assume you created your seed database in WAL mode. This means that you likely missed the -wal and -shm files. If you specified that any data attributes should use external storage, you missed that data as well.
When creating a seed database that you will copy, you need to also copy the -shm/wal files, or you need to create the database in rollback journal mode, which will have all added data in the file.
You do that by setting the following option when adding the persistent store (if you already use options, just incorporate this one in with the others).
NSDictionary *options = #{NSSQLitePragmasOption:#{#"journal_mode":#"DELETE"}};
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error]) {
// Handle error
}
This is how you would add the persistent store when you are creating the database that you want to copy to the bundle. It will ensure that everything is in the actual database file.
Now, after you run your code to create your seed file, you should go to that directory and see if there are any other files laying around. The best way to do this is in Terminal. Go to that directory and do "ls -lat" which will list all files sorted by time.
Caveat: Both SQLite and Core Data can create extra files that go with the sqlite file, depending on configuration of both entities. Thus, you should, in general, treat each core data persistent store as a file package. In other words, you should create a separate directory just for the core data store. That way, when either SQLite or core data decides to create extra files, everything is confined to a single directory.
When you copy the files into the bundle resources, from where you created your seed database, you need to make sure you copy everything.
Likewise, you should copy everything from the bundle to the device when deploying.
However, instead of using the file system, you should consider these methods on NSPersistentStoreCoordinator...
/* Used for save as - performance may vary depending on the type of old and
new store; the old store is usually removed from the coordinator by the
migration operation, and therefore is no longer a useful reference after
invoking this method
*/
- (NSPersistentStore *)migratePersistentStore:(NSPersistentStore *)store
toURL:(NSURL *)URL
options:(nullable NSDictionary *)options
withType:(NSString *)storeType
error:(NSError **)error;
/* copy or overwrite the target persistent store in accordance with the store
class's requirements. It is important to pass similar options as
addPersistentStoreWithType: ... SQLite stores will honor file locks, journal
files, journaling modes, and other intricacies. Other stores will default
to using NSFileManager.
*/
- (BOOL)replacePersistentStoreAtURL:(NSURL *)destinationURL
destinationOptions:(nullable NSDictionary *)destinationOptions
withPersistentStoreFromURL:(NSURL *)sourceURL
sourceOptions:(nullable NSDictionary *)sourceOptions
storeType:(NSString *)storeType
error:(NSError**)error NS_AVAILABLE(10_11, 9_0);
Thus, I believe that if you create your database properly, then copy it fully and properly to both your resource bundle and into your application, then your issues will go away.
I got this error when I was the creating/updating coredata entity on background thread, doing the same on main thread resolved the issue.
I'm working on an app that using core data, and everything is working so far. But I haven't used an image in it yet. I would like to do so but I don't know where to start. I would like to have the user click a button that gives them the option of locations of where they can get images. Also, is it possible to have a user just enter the URL of an image and have it save to core data? Thats really it so if someone could point me in the right direction I would appreciate it. Thanks!
My code so far: https://github.com/jackintosh7/Core-Data
EDIT - I updated some of the references in the saveImgData:fromURL to match the parameters passed in the method. I used a variation of this method within an NSURL protocol to intercept requests and cache specific ones. Some of the entity parameters may not apply to your question. Just disregard those (such as encoding/lastModified/mimeType/response).
To save an image to CoreData give this a try.
First, download your image. A user can enter the url in a text entry field. (The example listed by andrewbuilder will work just fine)
NSURL *imgURL = [NSURL URLWithString:myImgURL];
NSData *imgData = [NSData dataWithContentsOfURL:imgURL];
//a better way would be to do this asynchronously. Google "Lazy Image Loading" and
//you should find a suitable example app from Apple
Setup your CoreData entity to store the image data. Here is a sample that stores more than just the image binary data.
Then save your image data directly to CoreData.
I have a CoreData manager class but you may have your CoreData stack in AppDelegate which is how Apple sets them up.
-(void)saveImgData:(NSData*)myImgData fromURL:(NSString*)urlStr{
//If you have a CoreData manager class do something like this
CoreDataManager *cdm = [CoreDataManager defaultManager];
//Use private queue concurrency type for background saving
NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
//Set up the parent context, in this case is the mainMOC
//If you are using basic CoreData it would be the managedObjectContext on your AppDelegate
ctx.parentContext = cdm.mainMOC;
[ctx performBlock:^{
//performBlock executes on a background thread
CachedURLResponse *cacheResponse = [NSEntityDescription insertNewObjectForEntityForName:#"CachedURLResponse" inManagedObjectContext:ctx];
cacheResponse.relativeURLHash = [NSNumber numberWithInteger:[urlStr hash]];
cacheResponse.data = [myImgData copy];
cacheResponse.response = [NSKeyedArchiver archivedDataWithRootObject:self.response];
cacheResponse.url = [urlStr copy];
cacheResponse.timestamp = [NSDate date];
cacheResponse.mimeType = [self.response.MIMEType copy];
cacheResponse.encoding = [self.response.textEncodingName copy];
if ([self.headers objectForKey:#"Last-Modified"])
cacheResponse.lastModified = [self.headers objectForKey:#"Last-Modified"];
NSError *__block error;
if (![ctx save:&error])
NSLog(#"Error, Cache not saved: %#", error.userInfo);
[cdm.mainMOC performBlockAndWait: ^{
[cdm saveContext];
}];
}];
}
My CoreDataManager.h looks like this:
and my CoreDataManager.m file looks like this:
#import "CoreDataManager.h"
#import <CoreData/CoreData.h>
#implementation CoreDataManager
#synthesize mainMOC = _mainMOC,
managedObjectModel = _mom,
persistentStoreCoordinator = _psc
;
+(CoreDataManager*)defaultManager
{
static CoreDataManager *_defaultMgr = nil;
static dispatch_once_t oncePred;
dispatch_once(&oncePred, ^{
_defaultMgr = [[CoreDataManager alloc] init];
});
return _defaultMgr;
}
-(id)init
{
if (self = [super init])
{
NSError *error = nil;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"MyDatabase.sqlite"];
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"MyDataModelName" withExtension:#"momd"];
_mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
_psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
_mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainMOC setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}
return self;
}
- (void)saveContext
{
NSError *error = nil;
if (self.mainMOC != nil && ([self.mainMOC hasChanges] && ![self.mainMOC save:&error])) {
NSLog(#"CoreData save error: %#, %#", error, [error userInfo]);
}
}
#pragma mark - Core Data Stack
- (NSManagedObjectContext *)mainMOC
{
return _mainMOC;
}
- (NSManagedObjectModel *)managedObjectModel
{
return _mom;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
return _psc;
}
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
Hope that gives you a good starting point. Google "CoreData Class Reference" for some really good reading on the topic.
I learnt a lot from Paul Hegarty's excellent iTunes U / Stanford Uni lectures... his latest is called "Developing iOS Apps for iPhone and iPad" A couple of his lectures specifically address the use of images in Core Data. For what you are wanting to do I recommend you watch lectures 10-13, but really it is worth watching the entire series.
Beyond that, spend some time to read through the Apple Documentation on Core Data, starting here. Samir writes a good response to this stack overflow question.
You have a few options in how you implement the code. While Core Data is capable of managing the saving of images into the structure of your persistent data store (e.g. sqlite), it is important to note that large image files will slow down the fetch process.
Core Data can manage how to store the image for you... by clicking on the Allows External Storage option in the Data Model Inspector... see attached image below.
If however, you are looking to point a user to a URL, it may be worth considering adding (another separate) attribute to your entity Item, for example an attribute named pictureURL.
Use a property in a UIViewController to manage the entity attribute...
#property (nonatomic, retain) NSString * pictureURL;
Then you can add a UITextField to a UIViewController to accept user input for this property pictureURL.
When you have a URL for an image, you can return the image with "getter" code similar to this (where the UIImage is a property of the UIViewController)...
- (UIImage *)image {
NSURL *imageURL = [NSURL URLWithString:self.pictureURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
return [UIImage imageWithData:imageData];
}
Hope that provides you with some direction.
I'm trying to perform an iOS Core Data migration that requires a MappingModel. Core data is not able to use the mapping model for some reason and it falls back to an automatic lightweight migration.
I enabled the MigrationDebug option to get more info and what I see makes no sense. The source and destination hashes of the mapping model are identical, ignoring order, to the source and destination ManagedObjectModels. It seems like the mapping model should be used but the log says "no suitable mapping model found".
Here is the (elided) log:
CoreData: annotation: (migration) will attempt automatic schema migration
CoreData: annotation: (migration) looking for mapping model with
source hashes:
{
TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
TSBuyer = <91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>;
...
}
destination hashes: {
TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
TSBuyer = <e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>;
...
}
CoreData: annotation: (migration) checking mapping model at path file://localhost/Users/xandrews/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/0A84951E-21FC-47C0-A1B7-F880ACB672C4/Dev.app/Migrate_0_5_24To_0_5_27.cdm
source hashes:
{(
<4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
<91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>,
...
)}
destination hashes: {(
<4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
<e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>,
...
)}
CoreData: annotation: (migration) no suitable mapping model found
CoreData: annotation: (migration) inferring a mapping model between data models with
source hashes: ...
The mapping model generated by Xcode 4 does not produce the correct hashes needed for the migration to occur. You can compare the output from the migration log to your mapping file's hashes with the code below:
NSString *mappingModelPath = [[NSBundle mainBundle] pathForResource:#"MappingFile" ofType:#"cdm"];
NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:mappingModelPath]];
for (NSEntityMapping *entityMapping in mappingModel.entityMappings) {
NSLog(#"%#: %#", entityMapping.sourceEntityName, entityMapping.sourceEntityVersionHash);
NSLog(#"%#: %#", entityMapping.destinationEntityName, entityMapping.destinationEntityVersionHash);
}
You'll see that these do not match the hashes in the migration log output.
The workaround is to generate the mapping file in Xcode 5.
In your persistentStoreCoordinator method give this line of code
NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption, nil];
If this doesnt help then you need to go for user implemented migration. So you will have to create a mapping model using source and destination model.
In that case set,
NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:NO],NSInferMappingModelAutomaticallyOption, nil];
Create douce metadata with following code
if (sourceMetadata) {
NSString *configuration = nil;
NSManagedObjectModel *destinationModel = [self.persistentStoreCoordinator managedObjectModel];
//Our Source 1 is going to be incompatible with the Version 2 Model, our Source 2 won't be...
BOOL pscCompatible = [destinationModel isConfiguration:configuration compatibleWithStoreMetadata:sourceMetadata];
NSLog(#"Is the STORE data COMPATIBLE? %#", (pscCompatible==YES) ?#"YES" :#"NO");
if (pscCompatible == NO) {
migrationSuccess = [self performMigrationWithSourceMetadata:sourceMetadata toDestinationModel:destinationModel];
}
}
else {
NSLog(#"checkForMigration FAIL - No Source Metadata! \nERROR: %#", [error localizedDescription]);
}
and implement the following function
- (BOOL)performMigrationWithSourceMetadata :(NSDictionary *)sourceMetadata toDestinationModel:(NSManagedObjectModel *)destinationModel
{
BOOL migrationSuccess = NO;
//Initialise a Migration Manager...
NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil
forStoreMetadata:sourceMetadata];
//Perform the migration...
if (sourceModel) {
NSMigrationManager *standardMigrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel
destinationModel:destinationModel];
//Retrieve the appropriate mapping model...
NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil
forSourceModel:sourceModel
destinationModel:destinationModel];
if (mappingModel) {
NSError *error = nil;
NSString *storeSourcePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"Recipes.sqlite"];
NSURL *storeSourceUrl = [NSURL fileURLWithPath: storeSourcePath];
NSString *storeDestPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"Recipes2.sqlite"];
NSURL *storeDestUrl = [NSURL fileURLWithPath:storeDestPath];
//Pass nil here because we don't want to use any of these options:
//NSIgnorePersistentStoreVersioningOption, NSMigratePersistentStoresAutomaticallyOption, or NSInferMappingModelAutomaticallyOption
NSDictionary *sourceStoreOptions = nil;
NSDictionary *destinationStoreOptions = nil;
migrationSuccess = [standardMigrationManager migrateStoreFromURL:storeSourceUrl
type:NSSQLiteStoreType
options:sourceStoreOptions
withMappingModel:mappingModel
toDestinationURL:storeDestUrl
destinationType:NSSQLiteStoreType
destinationOptions:destinationStoreOptions
error:&error];
NSLog(#"MIGRATION SUCCESSFUL? %#", (migrationSuccess==YES)?#"YES":#"NO");
}
}
else {
//TODO: Error to user...
NSLog(#"checkForMigration FAIL - No Mapping Model found!");
abort();
}
return migrationSuccess;
}
After futher investigation I found out I was experiencing the same issue as mentioned here (Core Data migration fails for to-one relationship).
Settings the minimum to 1 instead of no minimum in my relationship made Core Data use my custom mapping model. While I'm not sure I assume this is a bug in Core Data.
However, this required me to change the original (1.0) mapping model (which users were already using) too.
The fix I came up with is to create a new mapping model between 1.0 and 2.0 called 1.5. The only thing that's different in 1.5 in comparison with 1.0 is the minimum of the relationship, which is in 1.5 set to 1.
Now, I could let Core Data perform a lightweight migration from 1.0 to 1.5 and hereafter perform my own custom migration from 1.5 to 2.0.
Although it may be a hacky solution, it works perfectly. I pasted the code that handles the migration below.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"Project.sqlite"]];
NSError *error;
NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeUrl error:&error];
if (! [[self managedObjectModel] isConfiguration:nil compatibleWithStoreMetadata:storeMetadata]) {
// The current store isn't compatible with the model and should be migrated, check the version identifier of the store
NSManagedObjectModel *sourceManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMetadata];
if ([[sourceManagedObjectModel versionIdentifiers] containsObject:#"Model_1_0"]) {
// The current version of the store is 1.0, a manual migration to 1.5 is needed in order to migrate finally to 2.0
NSURL *destinationModelURL = [[NSBundle mainBundle] URLForResource:#"Model.momd/Model_1_5" withExtension:#"mom"];
NSManagedObjectModel *destinationManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL];
NSMigrationManager *migrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceManagedObjectModel destinationModel:destinationManagedObjectModel];
NSMappingModel *inferredMappingModel = [NSMappingModel inferredMappingModelForSourceModel:sourceManagedObjectModel destinationModel:destinationManagedObjectModel error:&error];
NSURL *destinationURL = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"Migration.sqlite"]];
[migrationManager migrateStoreFromURL:storeUrl
type:NSSQLiteStoreType
options:nil
withMappingModel:inferredMappingModel
toDestinationURL:destinationURL
destinationType:NSSQLiteStoreType
destinationOptions:nil
error:&error];
if (error) {
DLog(#"Failed to migrate store from URL %# with mapping model %# to destination URL %# with error %#", storeUrl, inferredMappingModel, destinationURL, error);
}
[[NSFileManager defaultManager] removeItemAtURL:storeUrl error:&error];
[[NSFileManager defaultManager] moveItemAtURL:destinationURL toURL:storeUrl error:&error];
}
}
NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption: [NSNumber numberWithBool:YES],
NSInferMappingModelAutomaticallyOption: [NSNumber numberWithBool:NO] };
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
/*Error for store creation should be handled in here*/
DLog(#"%#", error);
}
return persistentStoreCoordinator;
}
The mapping model is probably not sufficient to handle the migration. In this case the mapping model will not be loaded, even if it matches source and destination model.
Write a test for the migration. Redo the changes to your model step by step and test the migration. That way you will find the change that does prevent the mapping model from loading.
Lookout for:
Renaming attributes or creating attributes without specifying default values.
Changing attributes to being non optional.
In those cases you have to specify the behavior manually within the mapping model.
Had the same issue. I deleted an entity and renamed the relationship fields accordingly. I first tried to used lightweight migration and therefore specified renaming IDs for the relationships. Due to an oversight I mixed up the fields used for the "Renaming ID" and the "Hash Modifier". Once corrected everything works as expected.
Do you know of any way to delete all of the entries stored in Core Data? My schema should stay the same; I just want to reset it to blank.
Edit
I'm looking to do this programmatically so that a user can essentially hit a reset button.
You can still delete the file programmatically, using the NSFileManager:removeItemAtPath:: method.
NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
Then, just add the persistent store back to ensure it is recreated properly.
The programmatic way for iterating through each entity is both slower and prone to error. The use for doing it that way is if you want to delete some entities and not others. However you still need to make sure you retain referential integrity or you won't be able to persist your changes.
Just removing the store and recreating it is both fast and safe, and can certainly be done programatically at runtime.
Update for iOS5+
With the introduction of external binary storage (allowsExternalBinaryDataStorage or Store in External Record File) in iOS 5 and OS X 10.7, simply deleting files pointed by storeURLs is not enough. You'll leave the external record files behind. Since the naming scheme of these external record files is not public, I don't have a universal solution yet. – an0 May 8 '12 at 23:00
You can delete the SQLite file - but I choose to do it by purging the tables individually with a functions:
- (void) deleteAllObjects: (NSString *) entityDescription {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityDescription inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
for (NSManagedObject *managedObject in items) {
[_managedObjectContext deleteObject:managedObject];
DLog(#"%# object deleted",entityDescription);
}
if (![_managedObjectContext save:&error]) {
DLog(#"Error deleting %# - error:%#",entityDescription,error);
}
}
The reason I chose to do it table by table is that it makes me confirm as I am doing the programming that deleting the contents of the table is sensible and there is not data that I would rather keep.
Doing it this will is much slower than just deleting the file and I will change to a file delete if I this method takes too long.
Updated Solution for iOS 10+
Use NSBatchDeleteRequest to delete all objects in the entity without having to load them into memory or iterate through them.
// create the delete request for the specified entity
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = MyEntity.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
// get reference to the persistent container
let persistentContainer = (UIApplication.shared.delegate as! AppDelegate).persistentContainer
// perform the delete
do {
try persistentContainer.viewContext.execute(deleteRequest)
} catch {
print(error.localizedDescription)
}
This code has been updated for iOS 10 and Swift 3. If you need to support iOS 9, see this question.
Sources:
Core Data: Quickest way to delete all instances of an entity (includes Objective-C code)
What's New in Core Data (WWDC 2015 video)
What's New in Core Data (WWDC 2016 video)
How to Use Core Data in iOS 10
What’s new in Core Data Swift 3.0
I've written a clearStores method that goes through every store and delete it both from the coordinator and the filesystem (error handling left aside):
NSArray *stores = [persistentStoreCoordinator persistentStores];
for(NSPersistentStore *store in stores) {
[persistentStoreCoordinator removePersistentStore:store error:nil];
[[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:nil];
}
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
This method is inside a coreDataHelper class that takes care of (among other things) creating the persistentStore when it's nil.
I remove all data from core data on a button Event in a HomeViewController class:
This article helped me so much I figured I'd contribute.
-(IBAction)buttonReset:(id)sender
{
NSLog(#"buttonReset Pressed");
//Erase the persistent store from coordinator and also file manager.
NSPersistentStore *store = [self.persistentStoreCoordinator.persistentStores lastObject];
NSError *error = nil;
NSURL *storeURL = store.URL;
[self.persistentStoreCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
NSLog(#"Data Reset");
//Make new persistent store for future saves (Taken From Above Answer)
if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// do something with the error
}
}
Note that in order to call self.persistentStoreCoordinator I declared a property in the Home View Controller. (Don't worry about the managedObjectContext that I use for saving and loading.)
#property (nonatomic, retain) NSManagedObjectContext * managedObjectContext;
#property (nonatomic, retain) NSPersistentStoreCoordinator * persistentStoreCoordinator;
Then in the AppDelegate ApplicationDidFinishLaunching right below creating a HomeViewController I have :
homeViewController = [[HomeViewController alloc] initWithNibName:#"HomeViewController" bundle:nil];
homeViewController.managedObjectContext = self.managedObjectContext;
homeViewController.persistentStoreCoordinator = self.persistentStoreCoordinator;
iOS9+, Swift 2
Delete all objects in all entities
func clearCoreDataStore() {
let entities = managedObjectModel.entities
for entity in entities {
let fetchRequest = NSFetchRequest(entityName: entity.name!)
let deleteReqest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try context.executeRequest(deleteReqest)
} catch {
print(error)
}
}
}
MagicalRecord makes this very easy.
[MyCoreDataObject MR_truncateAll];
[Late answer in response to a bounty asking for newer responses]
Looking over earlier answers,
Fetching and deleting all items, as suggested by #Grouchal and others, is still an effective and useful solution. If you have very large data stores then it might be slow, but it still works very well.
Simply removing the data store is, as you and #groundhog note, no longer effective. It's obsolete even if you don't use external binary storage because iOS 7 uses WAL mode for SQLite journalling. With WAL mode there may be (potentially large) journal files sitting around for any Core Data persistent store.
But there's a different, similar approach to removing the persistent store that does work. The key is to put your persistent store file in its own sub-directory that doesn't contain anything else. Don't just stick it in the documents directory (or wherever), create a new sub-directory just for the persistent store. The contents of that directory will end up being the persistent store file, the journal files, and the external binary files. If you want to nuke the entire data store, delete that directory and they'll all disappear.
You'd do something like this when setting up your persistent store:
NSURL *storeDirectoryURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"persistent-store"];
if ([[NSFileManager defaultManager] createDirectoryAtURL:storeDirectoryURL
withIntermediateDirectories:NO
attributes:nil
error:nil]) {
NSURL *storeURL = [storeDirectoryURL URLByAppendingPathComponent:#"MyApp.sqlite"];
// continue with storeURL as usual...
}
Then when you wanted to remove the store,
[[NSFileManager defaultManager] removeItemAtURL:storeDirectoryURL error:nil];
That recursively removes both the custom sub-directory and all of the Core Data files in it.
This only works if you don't already have your persistent store in the same folder as other, important data. Like the documents directory, which probably has other useful stuff in it. If that's your situation, you could get the same effect by looking for files that you do want to keep and removing everything else. Something like:
NSString *docsDirectoryPath = [[self applicationDocumentsDirectory] path];
NSArray *docsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docsDirectoryPath error:nil];
for (NSString *docsDirectoryItem in docsDirectoryContents) {
// Look at docsDirectoryItem. If it's something you want to keep, do nothing.
// If it's something you don't recognize, remove it.
}
This approach may be error prone. You've got to be absolutely sure that you know every file you want to keep, because otherwise you might remove important data. On the other hand, you can remove the external binary files without actually knowing the file/directory name used to store them.
Here is combined solution for purging Core Data.
- (void)deleteAllObjectsInCoreData
{
NSArray *allEntities = self.managedObjectModel.entities;
for (NSEntityDescription *entityDescription in allEntities)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entityDescription];
fetchRequest.includesPropertyValues = NO;
fetchRequest.includesSubentities = NO;
NSError *error;
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Error requesting items from Core Data: %#", [error localizedDescription]);
}
for (NSManagedObject *managedObject in items) {
[self.managedObjectContext deleteObject:managedObject];
}
if (![self.managedObjectContext save:&error]) {
NSLog(#"Error deleting %# - error:%#", entityDescription, [error localizedDescription]);
}
}
}
If you want to delete all objects and do not want to delete the backing files, you can use following methods:
- (void)deleteAllObjectsInContext:(NSManagedObjectContext *)context
usingModel:(NSManagedObjectModel *)model
{
NSArray *entities = model.entities;
for (NSEntityDescription *entityDescription in entities) {
[self deleteAllObjectsWithEntityName:entityDescription.name
inContext:context];
}
}
- (void)deleteAllObjectsWithEntityName:(NSString *)entityName
inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:entityName];
fetchRequest.includesPropertyValues = NO;
fetchRequest.includesSubentities = NO;
NSError *error;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *managedObject in items) {
[context deleteObject:managedObject];
NSLog(#"Deleted %#", entityName);
}
}
Beware that it may be very slow (depends on how many objects are in your object graph).
If you want to go the delete all objects route (which is much simpler than tearing down the Core Data stack, but less performant), than this is a better implementation:
- (void)deleteAllManagedObjectsInModel:(NSManagedObjectModel *)managedObjectModel context:(NSManagedObjectContext *)managedObjectContext
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[managedObjectContext performBlockAndWait:^{
for (NSEntityDescription *entity in managedObjectModel) {
NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesSubentities:NO];
NSArray *objects = [managedObjectContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *managedObject in objects) {
[managedObjectContext deleteObject:managedObject];
}
}
[managedObjectContext save:nil];
}];
}];
[operation setCompletionBlock:^{
// Do stuff once the truncation is complete
}];
[operation start];
}
This implementation leverages NSOperation to perform the deletion off of the main thread and notify on completion. You may want to emit a notification or something within the completion block to bubble the status back to the main thread.
iOS 10 + Swift 3 solution:
func clearCoreDataStore() {
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
for i in 0...delegate.persistentContainer.managedObjectModel.entities.count-1 {
let entity = delegate.persistentContainer.managedObjectModel.entities[i]
do {
let query = NSFetchRequest<NSFetchRequestResult>(entityName: entity.name!)
let deleterequest = NSBatchDeleteRequest(fetchRequest: query)
try context.execute(deleterequest)
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
Iterates through all of the core data entities and clears them
Swift 4/5, iOS 9+
Rebuilding the whole CoreData SQLite file will make sure all data is erased, therefore all entities are deleted. Just call deleteAndRebuild().
class CoreDataStack {
// Change this
static let datamodelName = "ProjectName"
static let storeType = "sqlite"
static let persistentContainer = NSPersistentContainer(name: datamodelName)
private static let url: URL = {
let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).\(storeType)")
assert(FileManager.default.fileExists(atPath: url.path))
return url
}()
static func loadStores() {
persistentContainer.loadPersistentStores(completionHandler: { (nsPersistentStoreDescription, error) in
if let error = error {
fatalError(error.localizedDescription)
}
})
}
static func deleteAndRebuild() {
try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: url, ofType: storeType, options: nil)
loadStores()
}
}
Thanks for the post. I followed it and it worked for me. But I had another issue that was not mentioned in any of the replies. So I am not sure if it was just me.
Anyway, thought I would post here the problem and my way that solved it.
I had a few records in the database, I wanted to purge everything clean before write new data to the db, so I did everything including
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
and then used managedObjectContext to access the database (supposed to be empty by now), somehow the data was still there.
After a while of troubleshooting, I found that I need to reset managedObjectContext, managedObject, managedObjectModel and
persistentStoreCoordinator, before I use managedObjectContext to access the dabase. Now I have a clean database to write to.
Here is a somewhat simplified version with less calls to AppDelegate self and the last bit of code that was left out of the top rated answer. Also I was getting an error "Object's persistent store is not reachable from this NSManagedObjectContext's coordinator" so just needed to add that back.
NSPersistentStoreCoordinator *storeCoordinator = [self persistentStoreCoordinator];
NSPersistentStore *store = [[storeCoordinator persistentStores] lastObject];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"dataModel"];
NSError *error;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
if (storeCoordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:storeCoordinator];
}
swift solution:
class func deleteAllManagedObjects() {
let modelURL = NSBundle.mainBundle().URLForResource("some string", withExtension: "mom")
let mom = NSManagedObjectModel(contentsOfURL: modelURL)
for entityName in mom.entitiesByName.keys {
let fr = NSFetchRequest(entityName: entityName as String)
let a = Utility.managedObjectContext().executeFetchRequest(fr, error: nil) as [NSManagedObject]
for mo in a {
Utility.managedObjectContext().deleteObject(mo)
}
}
Utility.managedObjectContext().save(nil)
}
As a quick reference to save searching elsewhere - recreating the persistent store after deleting it can be done with:
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// do something with the error
}
Several good answers to this question. Here's a nice concise one. The first two lines delete the sqlite database. Then the for: loop deletes any objects in the managedObjectContext memory.
NSURL *storeURL = [[(FXYAppDelegate*)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory] URLByAppendingPathComponent:#"AppName.sqlite"];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
for (NSManagedObject *ct in [self.managedObjectContext registeredObjects]) {
[self.managedObjectContext deleteObject:ct];
}
Here's a version that deletes every record in every table you have.
Swift 4
static func resetDatabase() {
do {
try dataStore.persistentStoreCoordinator.managedObjectModel.entities.forEach { (entity) in
if let name = entity.name {
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: name)
let request = NSBatchDeleteRequest(fetchRequest: fetch)
try mainContext.execute(request)
}
}
try mainContext.save()
} catch {
print("error resenting the database: \(error.localizedDescription)")
}
}
you can also find all the entity names, and delete them by name. Its a longer version but works well, that way you dont have to work with persistence store
- (void)clearCoreData
{
NSError *error;
NSEntityDescription *des = [NSEntityDescription entityForName:#"Any_Entity_Name" inManagedObjectContext:_managedObjectContext];
NSManagedObjectModel *model = [des managedObjectModel];
NSArray *entityNames = [[model entities] valueForKey:#"name"];
for (NSString *entityName in entityNames){
NSFetchRequest *deleteAll = [NSFetchRequest fetchRequestWithEntityName:entityName];
NSArray *matches = [self.database.managedObjectContext executeFetchRequest:deleteAll error:&error];
}
if (matches.count > 0){
for (id obj in matches){
[_managedObjectContext deleteObject:obj];
}
[self.database.managedObjectContext save:&error];
}
}
for "Any_Entity_Name" just give any one of your entity's name, we only need to figure out the entity description your entities are within. ValueForKey#"name" will return all the entity names. Finally, dont forget to save.
The accepted answer is correct with removing URL by NSFileManager is correct, but as stated in iOS 5+ edit, the persistent store is not represented only by one file. For SQLite store it's *.sqlite, *.sqlite-shm and *.sqlite-wal ... fortunately since iOS 7+ we can use method
[NSPersistentStoreCoordinator
+removeUbiquitousContentAndPersistentStoreAtURL:options:error:]
to take care of removal, so the code should be something like this:
NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSString *storeName = ...;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:storeURL.path options:#{NSPersistentStoreUbiquitousContentNameKey: storeName} error:&error];
One other method (apart from a delete batch request) I often use (based on app requirement) is to reset the persistent store. The implementation looks like this for iOS 10+ and Swift (assuming you have a CoreDataManager class):
let persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "<Data-Model-Name>“)
container.loadPersistentStores(completionHandler: { (storeDescription, err) in
if let err = err {
fatalError("loading of store failed: \(err)")
}
})
return container
}()
func resetPersistentStore() {
if let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.last {
let storeURL = persistentContainer.persistentStoreCoordinator.url(for: persistentStore)
do {
try persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: storeURL, ofType: NSSQLiteStoreType, options: nil)
} catch {
print("failed to destroy persistent store:", error.localizedDescription)
}
do {
try persistentContainer.persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
} catch {
print("failed to re-add persistent store:", error.localizedDescription)
}
}
}
One advantage of this method is that it’s more straightforward especially when you have loads of data record for numerous entities in your core data. In which case a delete batch request would be memory intensive.
Swift 5.1 Solution
public static func reset() {
let coordinator = _persistentContainer.persistentStoreCoordinator
for store in coordinator.persistentStores where store.url != nil {
try? coordinator.remove(store)
try? FileManager.default.removeItem(atPath: store.url!.path)
}
}
Works with all versions. Pass entity name and iterate through to delete all the entries and save the context.
func deleteData(entityToFetch: String, completion: #escaping(_ returned: Bool) ->()) {
var context = NSManagedObjectContext()
if #available(iOS 10.0, *) {
context = self.persistentContainer.viewContext
} else {
context = self.managedObjectContext
}
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityToFetch, in: context)
fetchRequest.includesPropertyValues = false
do {
let results = try context.fetch(fetchRequest) as! [NSManagedObject]
for result in results {
context.delete(result)
}
try context.save()
completion(true)
} catch {
completion(false)
print("fetch error -\(error.localizedDescription)")
}
}
There seems to be two approaches:
Say you have a typical singleton for your core data stack.
import CoreData
public let core = Core.shared
public final class Core {
static let shared = Core()
var container: NSPersistentContainer!
private init() {
container = NSPersistentContainer(name: "stuff")
//deleteSql()
container.loadPersistentStores { storeDescription, error in
if let error = error { print("Error loading... \(error)") }
}
//deleteAll()
}
func saveContext() { // typical save helper
if container.viewContext.hasChanges {
do { try container.viewContext.save()
} catch { print("Error saving... \(error)") }
}
}
then ...
func deleteSql() {
let url = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask)[0].appendingPathComponent( "stuff.sqlite" )
guard FileManager.default.fileExists(atPath: url.path) else {
print("nothing to delete!")
return
}
do {
try container.persistentStoreCoordinator.destroyPersistentStore(
at: url, ofType: "sqlite", options: nil)
print("totally scorched the sql file. you DO now have to LOAD again")
}
catch {
print("there was no sql file there!")
}
}
func deleteAll() { // courtesy #Politta
for e in container.persistentStoreCoordinator.managedObjectModel.entities {
let r = NSBatchDeleteRequest(
fetchRequest: NSFetchRequest(entityName: e.name ?? ""))
let _ = try? container.viewContext.execute(r)
}
saveContext()
print("conventionally deleted everything from within core data. carry on")
}
}
.
1. scorch the sql file
Courtesy the excellent #J.Doe answer. You completely destroy the sql file.
You must to do this
before loading core data, ie, after creating the container, but
before actually loading the stores.)
(Notice the example line of code "//deleteSql()" is just before initialization.)
2. delete everything one by one within core data
Courtesy the excellent #Politta answer. You could do this at any time once core data is up and running.
(Notice the example line of code "//deleteAll()" is after initialization.)
Approach one is probably more useful during development. Approach two is probably more useful in production (in the relatively unusual case that for some reason you need to wipe everything).
Delete the persistent store file and setup a new persistent store coordinator?
Delete sqlite from your fileURLPath and then build.
Assuming you are using MagicalRecord and have a default persistence store:
I don't like all the solutions that assume certain files to exist and/or demand entering the entities names or classes. This is a Swift(2), safe way to delete all the data from all the entities. After deleting it will recreate a fresh stack too (I am actually not sure as to how neccessery this part is).
It's godo for "logout" style situations when you want to delete everything but have a working store and moc to get new data in (once the user logs in...)
extension NSManagedObject {
class func dropAllData() {
MagicalRecord.saveWithBlock({ context in
for name in NSManagedObjectModel.MR_defaultManagedObjectModel().entitiesByName.keys {
do { try self.deleteAll(name, context: context) }
catch { print("⚠️ ✏️ Error when deleting \(name): \(error)") }
}
}) { done, err in
MagicalRecord.cleanUp()
MagicalRecord.setupCoreDataStackWithStoreNamed("myStoreName")
}
}
private class func deleteAll(name: String, context ctx: NSManagedObjectContext) throws {
let all = NSFetchRequest(entityName: name)
all.includesPropertyValues = false
let allObjs = try ctx.executeFetchRequest(all)
for obj in allObjs {
obj.MR_deleteEntityInContext(ctx)
}
}
}
Use this
+(NSArray *)fetchDataFromEntity:(NSString *)entityName context:(NSManagedObjectContext *)context
{
NSFetchRequest * fetchRequest =[[NSFetchRequest alloc] init];
NSEntityDescription * CategoriesEntity = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
[fetchRequest setEntity:CategoriesEntity];
NSError * error;
NSInteger count = [context countForFetchRequest:fetchRequest error:&error];
if (count && count>0) {
NSArray * fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects && fetchedObjects.count>0) {
return fetchedObjects;
}else
return nil;
}
else
return nil;
}
+ (void)deleteObjectsOfArray:(NSMutableArray*)ary context:(NSManagedObjectContext *)context {
for (NSManagedObject * obj in ary) {
[context deleteObject:obj];
}
NSError *saveError = nil;
[context save:&saveError];
}
+ (void)deleteEntity:(NSString *)entityName context:(NSManagedObjectContext *)context {
NSArray *listArray = [self fetchDataFromEntity:entityName context:context];
[self deleteObjectsOfArray:[NSMutableArray arrayWithArray:listArray] context:context];
}
I took Grouchal's code and to speed it up I used enumeration with concurrent mode (NSEnumerationConcurrent), it got a bit faster compared to for loop (in my app I added this feature for Testers so that they can clear data and do testcases rather than delete and install app)
- (void)resetObjects
{
[self deleteAllObjectsInEntity:#"Entity1"];
[self deleteAllObjectsInEntity:#"Entity2"];
[self deleteAllObjectsInEntity:#"Entity3"];
[self deleteAllObjectsInEntity:#"Entity4"];
}
-(void) deleteAllObjectsInEntity:(NSString*) entityName
{
MainDataContext *coreDataContext = [MainDataContext sharedInstance];
NSManagedObjectContext *currentContext = coreDataContext.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:currentContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [currentContext executeFetchRequest:fetchRequest error:&error];
[items enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSManagedObject * obj, NSUInteger idx, BOOL *stop) {
[currentContext deleteObject:obj];
}];
if (![currentContext save:&error]) {
NSLog(#"Error deleting %# - error:%#",entityName,error);
}
}