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.
Related
I need to move my app's database to a new location and stop it from being shared through iCloud automatically.
From my understanding, Apple previously documented that a database should be placed in the Documents directory of an application, but now says that the database should be placed in the Library directory. This is because iOS now shares all data from the Documents directory through iCloud.
My issue is that I need to move my database from the Documents directory to the Library directory. Below is the code I use to do this.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
// Get the URL of the persistent store
NSURL *oldURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"App.sqlite"];
bool oldStoreExists = [[NSFileManager defaultManager] fileExistsAtPath:[oldURL path]];
// Get the URL of the new App Group location
NSURL *newURL = [[self applicationLibraryDirectory] URLByAppendingPathComponent:#"App.sqlite"];
bool shouldMoveStore = false;
NSURL *storeURL = nil;
if (!oldStoreExists) {
//There is no store in the old location
storeURL = newURL;
} else {
storeURL = oldURL;
shouldMoveStore = true;
}
NSError *error = nil;
NSDictionary *options = #{ NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES };
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(#"Error Moving Store: %ld\nDescription: %#\nReason: %#\nSuggested Fix:%#", (long)error.code, error.localizedDescription, error.localizedFailureReason, error.localizedRecoverySuggestion);
abort();
}
if (shouldMoveStore)
[self movePersistentStoreFrom:oldURL To:newURL];
return _persistentStoreCoordinator;
}
-(void) movePersistentStoreFrom:(NSURL *)oldURL To:(NSURL *)newURL {
// Get the reference to the current persistent store
NSPersistentStore *oldStore = [_persistentStoreCoordinator persistentStoreForURL:oldURL];
// Migrate the persistent store
NSError *error = nil;
[_persistentStoreCoordinator migratePersistentStore:oldStore toURL:newURL options:nil withType:NSSQLiteStoreType error:&error];
if (error) {
NSLog(#"Error Moving Store: %ld\nDescription: %#\nReason: %#\nSuggested Fix:%#", (long)error.code, error.localizedDescription, error.localizedFailureReason, error.localizedRecoverySuggestion);
abort();
}
}
This code seems to work in moving the persistent store, but on the next run of the application the old store is found again and the app attempts to migrate the store a second time. At this point a crash occurs, and I'm assuming it is due to this double migration call. In classic Xcode style, the stack trace is pretty much useless.
Update -
The migratePersistentStore function does not remove ("cut/paste") the old sqlite database, instead it just makes a "copy". It looks like I'll need to remove the old one programmatically.
I switched the code to check if the newStoreExists as opposed to the !oldStoreExists, and the migration does work.
My new issue is that the table rows of the migrated data have become randomized. Any help with this would be appreciated!
Update 2 -
As it turns out, any data migrations like moving the store location or updating the database will randomize your data if you are using Core Data. You need to put an attribute into your table and use a NSSortDescriptor inside of your NSFetchRequest to manually sort the data and keep it ordered.
If you never migrate the data (I previously had not) the core data fetch request always come in the order it was saved.
I am using CoreData in an iOS application to manage words in a "flash cards" application for people learning new languages.
The problem I'm having is that when I set up the data for a new entity and try to save it to the data store, I get a violation of a UNIQUE CONSTRAINT requirement on the sqlite database. The column in question is the Z_PK column, which I understand to be a PRIMARY KEY created by the iOS core data methods when the data store is initially created.
Here's the UNIQUE CONSTRAINT message I'm getting when I attempt the save:
2015-03-14 09:25:14.427 ghoti[25856:1107373] CoreData: error: (1555)
UNIQUE constraint failed: ZGHOTIENTITY.Z_PK (lldb)
Z is the prefix stuck on all of these SQL columns
GHOTIENTITY is my data store
Z_PK is the primary key
Here's a quick screenshot of the data table inside the sqlite "editor" in firefox:
The definition of the business object entity itself does not include the Z_PK column:
#implementation ghotiEntity
#dynamic numberCorrect;
#dynamic numberIncorrect;
#dynamic likelihoodOfPresentingWord;
#dynamic englishText;
#dynamic hebrewText;
#dynamic phoneticText;
#end
I'd be annoyed - but compliant - if it were as simple as just setting the ghotiEntity.Z_PK to the maximum value of Z_PK in the datastore + 1, but that column is not even part of the entity definition in the first place.
EDIT - A helpful user asked for the code, so I'm adding it here rather than trying to stuff it into a comment:
I'm using the mmBusinessObject collection of core data methods that I've seen floating around the web. Kevin McNeish explains it fully here.
The basic structure is this:
mmBusinessObject.m and .h do a nice job of wrapping the managedObject methods into something "easy to read and deal with". For the sake of readability, I've copied the snippet of two elements [entity createEntities] and [entity saveEntities] here and the full .m file at the end of this post. (note that I have not yet built in all of the error-checking, I'm just trying to make the basic function work.
CREATE A NEW ENTITY:
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
SAVE THE CHANGES
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
So my operating code (inside my ViewController.m file) does this: (note that ghotiEntity is my specific business object.
- (IBAction)saveButtonAction:(UIButton *)sender {
ghotiEntity *newGhotiEntry = (ghotiEntity *)[ghotiData createEntity];
newGhotiEntry.englishText=self.englishWord.text;
newGhotiEntry.hebrewText=self.hebrewWord.text;
newGhotiEntry.phoneticText=self.phoneticWord.text;
newGhotiEntry.numberCorrect=0;
newGhotiEntry.numberIncorrect=0;
newGhotiEntry.likelihoodOfPresentingWord=0;
[ghotiData saveEntities];
[self enableRegularUI];
[self pickNextCard];
}
It is from the NSLog line in the saveEntities method above that I get the diagnostic information that it is the Z_PK column that has the UNIQUE CONSTRAINT problem.
As promised above, here is the full .m file. I need to make sure that it is clear that the credit for this code goes to either Kevin McNeish as noted above, or any of several other collaborators who have posted the same code in other places... just search mmBusinessObject in google. I think Kevin is the originator and his tutorial is really good. The first few lines in the versions I have seen all have Kevin's name in them!
#import "mmBusinessObject.h"
#implementation mmBusinessObject
// Initialization
- (id)init
{
if ((self = [super init])) {
_copyDatabaseIfNotPresent = YES;
}
return self;
}
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
// Delete the specified entity
- (void) deleteEntity:(NSManagedObject *)entity {
[self.managedObjectContext deleteObject:entity];
}
// Gets entities for the specified request
- (NSMutableArray *)getEntities: (NSString *)entityName sortedBy:(NSSortDescriptor *)sortDescriptor matchingPredicate:(NSPredicate *)predicate
{
NSError *error = nil;
// Create the request object
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Set the entity type to be fetched
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Set the predicate if specified
if (predicate) {
[request setPredicate:predicate];
}
// Set the sort descriptor if specified
if (sortDescriptor) {
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Execute the fetch
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
return mutableFetchResults;
}
// Gets all entities of the default type
- (NSMutableArray *)getAllEntities
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:nil];
}
// Gets entities of the default type matching the predicate
- (NSMutableArray *)getEntitiesMatchingPredicate: (NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Gets entities of the default type matching the predicate string
- (NSMutableArray *)getEntitiesMatchingPredicateString: (NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Get entities of the default type sorted by descriptor matching the predicate
- (NSMutableArray *)getEntitiesSortedBy: (NSSortDescriptor *) sortDescriptor
matchingPredicate:(NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:sortDescriptor matchingPredicate:predicate];
}
// Gets entities of the specified type sorted by descriptor, and matching the predicate string
- (NSMutableArray *)getEntities: (NSString *)entityName
sortedBy:(NSSortDescriptor *)sortDescriptor
matchingPredicateString:(NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:entityName sortedBy:sortDescriptor matchingPredicate:predicate];
}
- (void) registerRelatedObject:(mmBusinessObject *)controllerObject
{
controllerObject.managedObjectContext = self.managedObjectContext;
}
// Saves all changes (insert, update, delete) of entities
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
#pragma mark -
#pragma mark Core Data stack
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created from the application's model.
*/
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:self.dbName withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// If the sqlite database doesn't already exist, create it
// by copying the sqlite database included in this project
if (self.copyDatabaseIfNotPresent) {
// Get the documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsDir = paths[0];
// Append the name of the database to get the full path
NSString *dbcPath = [docsDir stringByAppendingPathComponent:
[self.dbName stringByAppendingString:#".sqlite"]];
// Create database if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbcPath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:self.dbName ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:dbcPath error:NULL];
}
}
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:
[NSString stringWithFormat:#"%#%#", self.dbName, #".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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
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:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
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(#"%#",storeURL);
if ([error code] == 134100) {
[self performAutomaticLightweightMigration];
}
else
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
return _persistentStoreCoordinator;
}
- (void)performAutomaticLightweightMigration {
NSError *error;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#%#", self.dbName, #".sqlite"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error]){
// Handle the error.
}
}
#pragma mark -
#pragma mark Application's Documents directory
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
I had the same problem with a Z_PK constraint issue. It stemmed from the fact that I had copied the empty sqlite database from my app's documents folder and prepopulated it using Firefox's SQLite manager add-in. The step I forgot was updating the Z_PrimaryKey table...you have to update the Z_MAX value for each table so that Core Data will know the next Z_PK to use when new records are added to the database. Simply set each Z_Max value with the number of records in each table. Save it and drop it back into your app and all will be well.
Look through your code for all places you create an object for the DB. Make sure that you are not creating objects from multiple threads.
i had the same issue but i had this when i want to update an entry , so i fixed with refreshObject then save like this:
[dataManager.managedObjectContext refreshObject:object mergeChanges:YES];
[dataManager saveContextWithBlock:block];
I have a Sqlite file with preloaded data. I am using Core Data to read and display data from the Sqlite file.
Here is how I am reading the data:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL;
NSString *path = [[NSBundle mainBundle] pathForResource:#"myAppname" ofType:#"sqlite"];
storeURL = [NSURL fileURLWithPath:path];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
// This dictionary automatically updates and migrates the store if it changes
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
[NSNumber numberWithBool: NO], NSReadOnlyPersistentStoreOption,
nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options 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.
*/
if (error) {
[myErrorAlert showError:error];
}
CLS_LOG(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
However I need to update the Sqlite file with new data which I did outside the app with sqlite data editor.
Now when I copy-paste and override "myAppname.sqlite" file, I get the following error:
CoreData: error: (14) I/O error for database at /Users/Username/Library/Application Support/iPhone Simulator/7.1/Applications/45C373D7-8A54-48EF-BA3C-2E2C6AB7467F/myAppname.app/myAppname.sqlite. SQLite error code:14, 'unable to open database file'
I did try to add NSDictionary *options = #{ NSSQLitePragmasOption : #{#"journal_mode" : #"DELETE"} but still the same error exists.
Also some answers suggested to remove reference to file and create a new one, but I have no idea how to do it.
Any idea?
EDIT
I have manage to get it working.
Open original app on iOS 6 simulator.
Repalce Sqlite file with new file and run App only iOS 6 simulator than run on iOS7 simulator. NO issue.
However still would like to know other comments.
I have followed a variety of posts here in SO to delete all the data from an app so I can start over. I have tried:
A) Deleting all the data:
NSArray *entities = model.entities;
for (NSEntityDescription *entityDescription in entities) {
[self deleteAllObjectsWithEntityName:entityDescription.name
inContext:context];
}
if ([context save:&error]) {
...
- (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);
}
}
B) Delete the physical data store:
NSError *error;
NSPersistentStore *store = [[self persistentStoreCoordinator].persistentStores lastObject];
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = store.persistentStoreCoordinator;
[self.diskManagedObjectContext reset]; // there is a local instance variable for the disk managed context
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
_diskManagedObjectContext = nil;
C) Perform step A and then step B
In all combinations it appears to run with no errors, but whenever I receive new data (via my HTTP service) and start adding it to the re-initialized data store I get all kinds of duplicate data and various data issues. I usually have to delete and reinstall the app to get the data clean enough to re-initialize.
It should be fairly straightforward. The user logs in. App data is downloaded and saved in the store. User logs out and logs in again or as different ID and new data is brought down.
Any ideas why the above methods are not working?
UPDATE:
I edited my code above to show that I am saving the context and removing the data store file. I still end up with bad leftover data. Could the problem be the multiple contexts we use? We have three contexts we use in the app: a UI-managed context, a background context and a disk-managed context. A notification listener takes care of merging changes in the background context with the disk managed context.
I have tried altering the above code to loop through the objects in all three contexts and we set them all to nil. The authentication code takes care of reinitializing the contexts. Still banging my head on what seems like a simple issue.
After
for (NSEntityDescription *entityDescription in entities) {
[self deleteAllObjectsWithEntityName:entityDescription.name
inContext:context];
}
Save your context
[context save:&error];
(B) doesn't delete the physical store, it just dissociates it from your app for the time being. No doubt you just attach it again shortly thereafter or upon next launch.
Use [[NSFileManager defaultManager] removeItemAtURL:... error:...] actually to delete the file from your disk.
As the other posters have said, you fail to NSManagedObjectContext -save: in (A) so you affect what's in that one context but not in the persistent store. Contexts are just in-memory scratch pads so as soon as you create a new context it'll be able to find everything in the persistent store again unless or until you save the one with the modifications.
I am trying to save some custom metadata to an iCloud-enabled Core Data SQLite store. The documentation appears to say that in addition to the metadata it generates, I should be able to set my own custom metadata - similar to NSUserPreferences, but attached to my Core Data store. It seems like it should be simple:
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[moc setPersistentStoreCoordinator:psc];
NSMutableDictionary* metadata = [[psc metadataForPersistentStore:store] mutableCopy];
metadata[#"testKey"] = #"testValue";
[psc setMetadata:metadata forPersistentStore:store];
[moc save:&error];
So after this runs my custom metadata key/value is visible in the store's metadata field. However, when I restart the app the metadata I added is no longer there (but the iCloud metadata is there). Note that this works correctly when using a regular non-iCloud store so it appears to be iCloud specific.
Additional information:
So lets say I get the metadata, add my custom fields and then save it using the static methods:
[NSPersistentStoreCoordinator setMetadata:metadata forPersistentStoreOfType:NSSQLiteStoreType URL:self.coreDataController.iCloudStore.URL error:&error];
NSDictionary* reviewMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:self.coreDataController.iCloudStore.URL error:&error];
The reviewMetadata variable contains the metadata with my added fields. However once I restart the app (and consequently reload the store) it's back to the way it was. I even tried setting a breakpoint and killing the app right after executing the above statements and when I restart the app my custom metadata is still gone.
Any ideas? Am I using metadata incorrectly? Does this not work with iCloud-enabled stores?
This seems to work for me but this is running on OS X. Have not tried the same on iOS.
- (void)setiCloudMetaDataForStore:(NSURL*)fileURL ofType:(NSString *)fileType iCloud:(bool)iCloudEnabled ubiquityName:(NSString*)ubiquityName {
//LOG(#"setiCloudMetaDataForStore called...");
NSError *error;
NSDictionary *metaData = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:fileType URL:fileURL error:&error];
if (!metaData) {
FLOG(#" problem getting metaData");
FLOG(#" - error is %#, %#", error, error.userInfo);
}
//FLOG(#" metaData is %#", metaData);
//Now see if we can add a setting to indicate it is iCloud enabled
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:metaData];
[dict setObject:[[NSNumber alloc] initWithBool:iCloudEnabled] forKey:#"iCloudEnabled"];
if (!iCloudEnabled)
[dict removeObjectForKey:NSPersistentStoreUbiquitousContentNameKey];
else
[dict setObject:ubiquityName forKey:NSPersistentStoreUbiquitousContentNameKey];
bool result = [NSPersistentStoreCoordinator setMetadata:dict forPersistentStoreOfType:fileType URL:fileURL error:&error];
if (!result) {
FLOG(#" problem setting metaData");
FLOG(#" - error is %#, %#", error, error.userInfo);
}
return;
}
I see that changes to metadata are (sometimes) kept in the -wal and -shm files, that apparently serve to speed up sqlite. I did not find another way to get my metadata changes in the .sqlite file than to add this option while opening the sqlite store:
NSDictionary *options = #{NSSQLitePragmasOption:#{#"journal_mode":#"DELETE"}};
Using this option the -wal and -shm file are not created, so everything must be committed to the .sqlite file.