Badal Shah Badal Shah - 1 month ago 16
iOS Question

IOS Coredata UNIQUE constraint failed:

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

}


Someone please help me ?
Note :- this all happen at first time. secondtime when i will run application all work correctly. don't know where is issue ?

Answer

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.

Comments