I have an app already in the App Store that uses core data to save data.
Now, when iOS 8 is about to come out I wanna add a widget to it, thus I must use App Groups to share data between the binaries.
One problem though - I need to change the store location to support App Groups to all the existing users.
I wrote the following code, trying to move the store to the new path:
// 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;
}
NSURL *oldStoreURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
oldStoreURL = [oldStoreURL URLByAppendingPathComponent:@"Schooler.sqlite"];
NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.schooler.mycontainer"];
storeURL = [storeURL URLByAppendingPathComponent:@"Schooler.sqlite"];
if([[NSFileManager defaultManager] fileExistsAtPath:oldStoreURL.path] == YES && [[NSFileManager defaultManager] fileExistsAtPath:storeURL.path] == NO)
{
// Prior today extension - Need to move to new directory
NSError *error = nil;
if([[NSFileManager defaultManager] moveItemAtURL:oldStoreURL toURL:storeURL error:&error] == YES)
NSLog(@"Migrated successfully to new database location.");
else
NSLog(@"error: %@",error);
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
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]);
}
return _persistentStoreCoordinator;
}
The output is always "Migrated successfully to new database location.", although all the data that was saved on the app before has been deleted, As if it created a new database instead of just moving it.
What causes the problem? How should I fix it?
Thank you.
The persistent store should be located in the AppData > Library > Application Support directory.
Yes. It's a pretty old framework and its Objective-C roots are not very well hidden. Regardless, Apple's clearly still interested in having people use and adopt Core Data given the fact that they've added new features for Core Data + SwiftUI in iOS 15, and previous iOS versions received various Core Data updates too.
Yes, there is always a SQLite behind it. You can find it in the documents folder. As with every new build a new documents folder is created on the simulator it's getting quite cumbersome to search it manually everytime. Create a function in your app and print it out.
Core Data is incredibly fast if you consider what it does under the hood to do its magic. But Realm is faster, much faster. Realm was built with performance in mind and that shows. As Marcus Zarra once said in a presentation, you don't choose Core Data for its speed.
A Core Data NSSQLiteStoreType store created with the default options is actually several files, as described in Technical Q&A 1809: New default journaling mode for Core Data SQLite stores in iOS 7 and OS X Mavericks. This is important to remember when attempting to move a store outside of a migration process, and is the source of your issue - you are moving one file when you need to be moving all of them. Moving the files individually outside of Core Data and without the benefits of a file coordinator is not recommended, however. It's much better to use a migration instead.
A migration will take the data from the source store and migrate it to your new store location, essentially replicating the old data at the new location. The old data will still exist on the filesystem. In your application, you should perform the migration as you are now, but do not attempt to move the old data to the new location yourself - that is where things are going wrong.
Instead of moving files around yourself, you can rely on a migration to move the data for you. First, add a store to the persistent store coordinator with the URL of the source data. Then you will perform a migration to move that data to the new URL
NSPersistentStore *sourceStore = nil;
NSPersistentStore *destinationStore = nil;
NSDictionary *storeOptions = @{ NSSQLitePragmasOption : @{ @"journal_mode" :
@"WAL" } };
// Add the source store
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldStoreURL options:storeOptions error:&error]){
// Handle the error
} else {
sourceStore = [coordinator persistentStoreForURL:oldStoreURL];
if (sourceStore != nil){
// Perform the migration
destinationStore = [coordinator migratePersistentStore:sourceStore toURL:storeURL options:storeOptions withType:NSSQLiteStoreType error:&error];
if (destinationStore == nil){
// Handle the migration error
} else {
// You can now remove the old data at oldStoreURL
// Note that you should do this using the NSFileCoordinator/NSFilePresenter APIs, and you should remove the other files
// described in QA1809 as well.
}
}
}
Once the migration has completed you can delete the old files. The example here explicitly specifies the SQLite journal options, this is to ensure that if the default options are changed in the future the code will still work. If you are using different options, you should use those instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With