Migrating iCloud to local persistent store - ios

I am running out of ideas how could I possibly migrate the iCloud store to local store.
From one side I get crash with message:
The specified persistent store was not found
on the other side
NSUnderlyingException = "Can't add the same store twice"
The point is there is no clear tutorial how to migrate stores (Or at least I can't find). Everything I found so far was pieces of information spread across net and couldn't make very much sense.
This is my code:
- (NSPersistentStoreCoordinator *) cloudPersistentStoreCoordinator {
self.storeURL = [[MMNUtilities localDocumentsURL] URLByAppendingPathComponent:#"myapp.sqlite"];
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSDictionary *storeOptions = #{NSPersistentStoreUbiquitousContentNameKey: #"myapp"};
NSError *error = nil;
self.persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:self.storeURL
options:storeOptions
error:&error];
if (error) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
- (BOOL) didMigrateiCloudStoreToLocalStore {
NSError *error = nil;
self.persistentStoreCoordinator = [self cloudPersistentStoreCoordinator];
NSDictionary *storeOptions = #{NSPersistentStoreUbiquitousContentNameKey: #"myapp",
NSPersistentStoreRemoveUbiquitousMetadataOption : #YES};
NSPersistentStore *localStore = [self.persistentStoreCoordinator migratePersistentStore:self.persistentStore <<< Exception happens here
toURL:self.storeURL
options:storeOptions
withType:NSSQLiteStoreType
error:&error];
if (error) {
NSLog(#"Error: %#", error.description);
[[NSNotificationCenter defaultCenter] postNotificationName:HnHCoreDataPersistentStoreMigrationFailed object:self];
return NO;
}
return [self reloadStore:localStore];
}
I got following message in console:
CoreData: error: -addPersistentStoreWithType:SQLite configuration:PF_DEFAULT_CONFIGURATION_NAME URL:file:///var/mobile/Containers/Data/Application/EFC750F6-D10E-4B8A-9F83-C77621218DB7/Documents/myapp.sqlite options:{
NSPersistentStoreRemoveUbiquitousMetadataOption = 1;
NSPersistentStoreUbiquitousContentNameKey = myapp;
"_NSNotifyObserversOfStoreChange" = 0;
} ... returned error Error Domain=NSCocoaErrorDomain Code=134080 "The operation couldn’t be completed. (Cocoa error 134080.)" UserInfo=0x15deee80 {NSUnderlyingException=Can't add the same store twice} with userInfo dictionary {
NSUnderlyingException = "Can't add the same store twice";
}
Any help will be appreciated.

You need to create a new local store with a different name.

Related

Custom Migration corrupts db

When I try to migrate my old db to a new db, it is all done correctly. But when I try to access the db, it says its corrupted or malformed. It happens only in iOS11.
I get this error -
error: exception handling request: <NSSQLRelationshipFaultRequestContext: 0x1c4cbc9e0> , Fatal error. The database at /var/mobile/Containers/Data/Application/743F0953-2F57-4B9F-931A-0E0AEF9E8D0A/Documents/Main.db is corrupted. SQLite error code:11, 'database disk image is malformed' with userInfo of {
NSFilePath = "/var/mobile/Containers/Data/Application/743F0953-2F57-4B9F-931A-0E0AEF9E8D0A/Documents/Main.db";
NSSQLiteErrorDomain = 11;
}
When I try accessing db, I get -
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
This is my code of migration -
NSError *error = nil;
NSMappingModel *mapping = [NSMappingModel inferredMappingModelForSourceModel: oldModel destinationModel: self error: &error];
if (error) {
LOG(#"Error while inferring mapping model: %#", error);
return NO;
}
NSString *newContextPath = [contextPath stringByAppendingPathExtension: #"tmp"];
NSValue *classValue = [[NSPersistentStoreCoordinator registeredStoreTypes] objectForKey: NSSQLiteStoreType];
Class sqliteStoreClass = (Class)[classValue pointerValue];
Class sqliteStoreMigrationManagerClass = [sqliteStoreClass migrationManagerClass];
NSURL *srcURL = [NSURL fileURLWithPath: contextPath], *dstURL = [NSURL fileURLWithPath: newContextPath];
NSMigrationManager *manager = [[sqliteStoreMigrationManagerClass alloc] initWithSourceModel: oldModel destinationModel: self];
#try {
if (![manager migrateStoreFromURL: srcURL type:NSSQLiteStoreType options:nil withMappingModel:mapping toDestinationURL: dstURL destinationType:NSSQLiteStoreType destinationOptions:nil error:&error]) {
LOG(#"Migration failed %#", error);
return NO;
}
} #catch (NSException *exception) {
LOG(#"Exception: %#", exception);
return NO;
}
if (![[NSFileManager defaultManager] removeItemAtPath: contextPath error: &error]) {
LOG(#"Error removing old database: %#", error);
return NO;
}
if (![[NSFileManager defaultManager] moveItemAtPath: newContextPath toPath: contextPath error: &error]) {
LOG(#"Error renaming/moving new database: %#", error);
return NO;
}
LOG(#"- Context Migration: Complete");
I finally found out, I was deleting only .db file but not .shm and .wal files. This was okay with iOS10 and below, but somehow corrupts the db in iOS11. So, deleted both the old .shm and .wal files fixed the issue.
If anyone struggling what is going wrong, this might be of some help.

When updating store, I get following error: NSUnderlyingException = "Cannot update object that was never inserted."

MY core data objects are following:
Author (name) >> Book(title) >> Notes(text, dateOfNote)
Relation boksByAuthor >> notesOnBook
+(void)persistBooksToDisk:(NSManagedObjectContext *)workerContext
subClass:(EECoreStack*)eeCoreStack
fromURL:(NSURL*)fileURL{
[workerContext performBlock:^{
NSArray * array = [AP_TextExtract componentSeperatedByBooksFromTXTFile:fileURL];
for (NSString *body in array) {
Note *note = (Note *) [NSEntityDescription insertNewObjectForEntityForName:#"Note" inManagedObjectContext:workerContext];
note.dateOfNote = [AP_TextExtract dateOfNoteFromString:body];
note.text = [AP_TextExtract noteTextFromString:body];
Book *book = (Book *) [NSEntityDescription insertNewObjectForEntityForName:#"Book" inManagedObjectContext:workerContext];
book.title = [AP_TextExtract bookNameFromString:body];
[book addNotesInBook: note];
Author *author = (Author*)[NSEntityDescription insertNewObjectForEntityForName:#"Author" inManagedObjectContext:workerContext];
author.name = [AP_TextExtract authorNameFromString:body];
[author addBooksByAuthor: book];
}];
[eeCoreStack saveTemporaryWorkerContext:workerContext];
}
saveTemporaryContext:
- (void)saveTemporaryWorkerContext:(NSManagedObjectContext *)context {
[context performBlock:^{
NSError *error;
[context save:&error];
if (error) {
NSLog(#"ERROR SAVING TEMPORARY WORKER MOC %#: %#:", [error localizedDescription], [error userInfo]);
}else {
[self saveMainContext];
}
}];
}
- (void)saveMainContext {
[self.mainContext performBlock:^{
NSError *error = nil;
[self.mainContext save:&error];
if(error){
NSLog(#"ERROR SAVING MAIN MOC %#: %#:", [error localizedDescription], [error userInfo]);
}else {
[self saveMasterContext];
}
}];
}
- (void)saveMasterContext {
[self.masterContext performBlock:^{
NSError *error = nil;
[self.masterContext save:&error];
if(error){
NSLog(#"ERROR SAVING MASTER CONTEXT %#; : %#", [error localizedDescription], [error userInfo]);
}
}];
[[NSNotificationCenter defaultCenter] postNotificationName:#"doneSavingMasterContext" object:nil];
}
The data is saved from Text File and saving follows pattern:
workerContext > mainContext > masterContext (Store)
First time data is saved successfully but later when it tries to save updated text file (with only change in note text), it throws out following error:
ERROR SAVING MASTER CONTEXT An error occurred while saving.
; : {
NSAffectedObjectsErrorKey = (
"<Note: 0x6000002d6570> (entity: Note; id: 0x600001424480 <x-coredata:///Note/t2371834C-1581-4A68-A56E-77E734B276CC1244> ; data:
text = \"quitely right );
NSUnderlyingException = "Cannot update object that was never inserted.";
}
One thing I noticed that after restarting app, the data is saved but again start to give same error after that.
Edit 1: this error only show if, I update existing attribute, not if I insert new instance of attribute.
When you are updating the data, first fetch the core data object using NSFetchRequest and then update the necessary data. Then make an attempt to save. You might be trying to update the data without fetching.

Cocoa OS X and iOS iCloud Coredata sync

I have an app for iOS and OSX which uses iCloud for syncing Coredata. In iOS its working well i think. The problems comes with the OSX version. The code used in the two versions is practically the same.
I have this in my AppDelegate:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"myStore.sqlite"];
NSURL *cloudRootURL=[fileManager URLForUbiquityContainerIdentifier:nil];
NSString *pathToCloudFile = [[cloudRootURL path]stringByAppendingPathComponent:#"Documents"];
pathToCloudFile = [pathToCloudFile stringByAppendingPathComponent:#"myAppCloudLogs1"];
NSURL *cloudURL = [NSURL fileURLWithPath:pathToCloudFile];
NSString *cloudStoreTitle = #"myStoreCloud";
NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES,
NSPersistentStoreUbiquitousContentURLKey: cloudURL,
NSPersistentStoreUbiquitousContentNameKey: cloudStoreTitle};
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:#selector(processStoresWillChange:)
name:NSPersistentStoreCoordinatorStoresWillChangeNotification
object:_persistentStoreCoordinator];
[notificationCenter addObserver:self
selector:#selector(processStoresDidChange:)
name:NSPersistentStoreCoordinatorStoresDidChangeNotification
object:_persistentStoreCoordinator];
[notificationCenter addObserver:self
selector:#selector(processContentChanges:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:_persistentStoreCoordinator];
return _persistentStoreCoordinator;
}
In both applications, when are started I can see the logs stored in the documents directory of the iCloud drive. There are 2 folders, 1 with my name (mac) and one with mobile simulator. When i create a new record in the simulator i can see new transaction log created in the simulator folder. But this change is not being reflected at my os x app. I think the transaction logs are not being replicated to other devices. Each device is only listening for changes in its folder. How can i do so each device listen for changes at others?
Is there any way so all the transaction logs are saved at the same folder for all devices?
When running the os x app i get this error message:
[PFUbiquitySetupAssistant canReadFromUbiquityRootLocation:]_block_invoke690(1489): CoreData: Ubiquity:
Attempting to download Peers hit a serious error for peers to download Error Domain=BRCloudDocsErrorDomain Code=5
No document at URL UserInfo=0x600000472f40 {NSDescription=No document at URL,
NSFilePath=/Users/myName/Library/Mobile Documents/iCloud~myGroup~myApp/Documents/myAppCloudLogs1/.DS_Store, NSUnderlyingError=0x600000251340 } with userInfo {
NSDescription = "No document at URL";
NSFilePath = "/Users/myName/Library/Mobile Documents/iCloud~myGroup~myApp/Documents/myAppCloudLogs1/.DS_Store";
NSUnderlyingError = "Error Domain=NSPOSIXErrorDomain Code=2 UserInfo=0x600000472fc0 {NSDescription=No such file or directory}";
}
Why am i getting this error?? What is .DS_Store file? In my ICloud Drive folders i only see .cdt files.
Need help please. Thanks in advance.

Core Data Automatic Lightweight Migration failing after moving sqlite file

in my app I've used Core Data Automatic Lightweight Migration fine for multiple versions. About a year ago I implemented iOS File Sharing so I moved the sqlite database to the /Library folder rather than the /Documents directory to keep it hidden. I haven't added another Core Data version since that time.
Now when I try to add another Entity and increase the model version I get the following error (truncated to show just the tail end):
"_NSAutoVacuumLevel" = 2;
}, reason=Can't find model for source store}
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason:
'This NSPersistentStoreCoordinator has no persistent stores.
It cannot perform a save operation.'
I've verified that the .sqlite file exists on disk as expected. I've been troubleshooting for a few days and it seems if I move the sqlite file back to the /Documents directory the migration works OK.
Below is the code for the persistent store coordinator:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self libraryDocumentsDirectory] stringByAppendingPathComponent: #"myappdb.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
//Pass options in for lightweight migration....
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]) {
/*Error for store creation should be handled in here*/
NSLog(#"ERROR IN MIGRATION........%#", error);
}
return persistentStoreCoordinator;
}
- (NSString *)libraryDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
}
Has anyone encountered this situation before?
Thanks in advance for your help.
"Now when I try to ad another model entity and increase the version number..."
You need to create a new model version before you create a new entity or make changes to existing entities. Make sure your changes were not made in the old model version by mistake.
After creating version and marking it as a current version you have to add an extra value in options which is used to add PersistentStore and then you go( I am not sure about other iOS version but yeah it will definitely work on iOS 7).
-(NSManagedObjectModel *)managedObjectModel
{
if (managedObjectModel != nil)
{
return managedObjectModel;
}
managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return managedObjectModel;
}
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (persistentStoreCoordinator != nil)
{
return persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"ABC.sqlite"];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] ini tWithManagedObjectModel:[self managedObjectModel]];
//Creating Lightweight migration.
NSDictionary *options =
#{
NSMigratePersistentStoresAutomaticallyOption:#YES
,NSInferMappingModelAutomaticallyOption:#YES
,NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}
};
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}

Every time I add something to the CoreData file i get sigabrt

Every time i add something to the CoreData (like adding an attribute to one of the entities) I get sigabrt and the only thing that helps is deleting the app from the emulator and cleaning the project.
I added an exception breakpoint and this is the function that breaks:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator == nil) {
NSURL *storeURL = [NSURL fileURLWithPath:[self dataStorePath]];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError *error;
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Error adding persistent store %#, %#", error, [error userInfo]);
abort();
}
}
return _persistentStoreCoordinator;
}
If I uncomment the abort() the app works but it wont access the data, how can I fix that so I can add an attribute without needing to erase everything every time?
EDIT:added the error.
2013-05-21 13:52:35.441 Game[23595:c07] Error adding persistent store Error Domain=NSCocoaErrorDomain Code=134100 "The operation couldn’t be completed. (Cocoa error 134100.)" UserInfo=0x7d6e5f0 {metadata={
NSPersistenceFrameworkVersion = 419;
NSStoreModelVersionHashes = {
GameTypeItem = <9b95d5f6 f27d2c7d 73452e34 c17e63a1 64bb657e 847085b3 12c5d3e0 17ea16b9>;
GameTypeLevelItem = <3224738f 99c7c8cf 4b23908a 356e345a 8b5d708a f68b7a2c f9de9ccb 0cec8fb8>;
SoundItem = <eb8cc3cf 0d6b83b4 8c01bb5b 3d2dc6a2 3688577c 4d73e2f4 7742c00e 56fd78de>;
};
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "F6FA6ED3-5663-4075-9D17-B38E4497468D";
"_NSAutoVacuumLevel" = 2;
}, reason=The model used to open the store is incompatible with the one used to create the store}, {
metadata = {
NSPersistenceFrameworkVersion = 419;
NSStoreModelVersionHashes = {
GameTypeItem = <9b95d5f6 f27d2c7d 73452e34 c17e63a1 64bb657e 847085b3 12c5d3e0 17ea16b9>;
GameTypeLevelItem = <3224738f 99c7c8cf 4b23908a 356e345a 8b5d708a f68b7a2c f9de9ccb 0cec8fb8>;
SoundItem = <eb8cc3cf 0d6b83b4 8c01bb5b 3d2dc6a2 3688577c 4d73e2f4 7742c00e 56fd78de>;
};
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "F6FA6ED3-5663-4075-9D17-B38E4497468D";
"_NSAutoVacuumLevel" = 2;
};
reason = "The model used to open the store is incompatible with the one used to create the store";
}
EDIT:Changed the function to this:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator == nil) {
NSURL *storeURL = [NSURL fileURLWithPath:[self dataStorePath]];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError *error;
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES} error:&error])
{
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
NSLog(#"Deleted old database %#, %#", error, [error userInfo]);
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES} error:&error];
abort();
}
}
return _persistentStoreCoordinator;
}
now it runs the program but it still doesnt show the data
You need to add the NSMigratePersistentStoresAutomaticallyOption & NSInferMappingModelAutomaticallyOption options:
Try this:
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES} error:&error]) {
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
NSLog(#"Deleted old database %#, %#", error, [error userInfo]);
[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES} error:&error];
}
NOTE: you need to turn on model versioning first and make sure that you create a new version of your data model each time you change it.
Read the apple documentation about model migration here.
It seems really obvious based on the error, but you need to cater for instances of when the original data store was created on earlier versions than the current version of Xcode.
The solution of deleting in my case wasn't required, but including the option to update the store worked a treat (via NSMigratePersistentStoresAutomaticallyOption)

Resources