Unable to add persistent store - ios

I'm trying to add a sqlite persistent store in Library/Application-Support folder as shown below:
- (NSManagedObjectModel *)managedObjectModel
{
if (!_managedObjectModel)
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"delete" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
}
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (!_persistentStoreCoordinator)
{
#synchronized(self)
{
if (!_persistentStoreCoordinator)
{
NSError *error = nil;
NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject] #"delete.sqlite"];
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSPersistentStore * persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
_persistentStoreCoordinator = persistentStoreCoordinator;
}
}
}
return _persistentStoreCoordinator;
}
This fails with the following error:
CoreData: error: -addPersistentStoreWithType:SQLite configuration:(null)
URL:file:///Users/harshithg/Library/Developer/CoreSimulator/Devices/D9A32558-558A-4E0E-915D-FEF25C772669/data/Containers/Data/Application/9517BBBB-E383-467E-B0E2-8960B734B239/Library/Application%20Support/delete.sqlite
options:(null) ...
returned error NSCocoaErrorDomain(512) with userInfo dictionary { reason = "Failed to create file; code = 2";
I'm on iOS 13.3 and Xcode 11.3. Does anyone know what's happening here?

The Application Support folder does not exist unless you explicitly create it. None of your code creates it. There's an NSFileManager method that will give you the NSApplicationSupportDirectory URL and create it (if it doesn't already exist) at the same time.
https://developer.apple.com/documentation/foundation/nsfilemanager/1407693-urlfordirectory?language=objc

Related

NSPersistentStoreCoordinator has no persistent stores (can't open)

I have my CoreDataManager Class and initialising Core Data Objects.
But when below piece of code runs it throws and error and cause crashing the app.
NSPersistentStoreCoordinator has no persistent stores (can't open)
When i debug the code it shows that the persistentStore object is actually nil.
Here is My CoreDataManager.m file
+ (id)sharedInstance {
static CoreDataManager *instance_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance_ = [[self alloc] init];
});
return instance_;
}
- (NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [NSManagedObjectContext new];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel alloc]initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"AppTutor" withExtension:#"momd"]];
return managedObjectModel;
}
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSString *documentsStorePath =
[[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent:#"AppTutor.sqlite"];
// if the expected store doesn't exist, copy the default store
if (![[NSFileManager defaultManager] fileExistsAtPath:documentsStorePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"AppTutor" ofType:#"sqlite"];
if (defaultStorePath) {
[[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:documentsStorePath error:NULL];
}
}
persistentStoreCoordinator =
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
// add the default store to our coordinator
NSError *error;
NSURL *defaultStoreURL = [NSURL fileURLWithPath:documentsStorePath];
NSPersistentStore *store = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:defaultStoreURL
options:nil
error:&error];
if (store == nil) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// setup and add the user's store to our coordinator
NSURL *userStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"AppTutor.sqlite"];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:userStoreURL
options:nil
error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return persistentStoreCoordinator;
}
Can you try to add these options to the addPersistentStoreWithType function?
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:userStoreURL
options:options
error:&error]

Core Data addPersistentStoreWithType return nil, but error is nil too

I'm creating a NSPersistentStore with the code below.
NSPersistentStore * pc = [persistentCoordinator
addPersistentStoreWithType:EncryptedStoreType
configuration:nil
URL:databaseURL
options:options
error:error];
if (*error)
{
NSLog(#"Unable to add persistent store.");
NSLog(#"Error: %#\n%#\n%#", *error, [*error userInfo], [*error localizedDescription]);
}
The value of options is
{
EncryptedStore = SQLite;
EncryptedStoreDatabaseLocation =
"file:///var/mobile/Containers/Data/Application/0C27F628-3FF0-467F-8EF1-5974EBBD3620/Documents/DBEncrypted.sqlite";
EncryptedStorePassphrase = "xxxxxxxxredactedxxxxxxx";
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1;
NSSQLitePragmasOption = {
synchronous = OFF;
};
}
At this point *error is nil and pc is nil too.
According to Apple's documentation if the function returns nil should be an error. Does anyone saw it before?
The EncryptedStoreType is from https://github.com/project-imas/encrypted-core-data
The error only happens if we are migrating the Data Store
EDIT:
Full code of method:
+ (NSPersistentStoreCoordinator *)makeStoreWithOptions:(NSDictionary *)options managedObjectModel:(NSManagedObjectModel *)objModel error:(NSError *__autoreleasing *)error
{
NSPersistentStoreCoordinator * persistentCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objModel];
// NSString* appSupportDir = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
BOOL backup = YES;
NSURL *databaseURL;
id dburl = [options objectForKey:EncryptedStoreDatabaseLocation];
if(dburl != nil) {
if ([dburl isKindOfClass:[NSString class]]){
databaseURL = [NSURL URLWithString:[options objectForKey:EncryptedStoreDatabaseLocation]];
backup = NO;
}
else if ([dburl isKindOfClass:[NSURL class]]){
databaseURL = dburl;
backup = NO;
}
}
if (backup){
NSString *dbNameKey = (__bridge NSString *)kCFBundleNameKey;
NSString *dbName = NSBundle.mainBundle.infoDictionary[dbNameKey];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationSupportURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
[fileManager createDirectoryAtURL:applicationSupportURL withIntermediateDirectories:NO attributes:nil error:nil];
databaseURL = [applicationSupportURL URLByAppendingPathComponent:[dbName stringByAppendingString:#".sqlite"]];
}
[persistentCoordinator addPersistentStoreWithType:EncryptedStoreType configuration:nil URL:databaseURL
options:options error:error];
if (*error)
{
NSLog(#"Unable to add persistent store.");
NSLog(#"Error: %#\n%#\n%#", *error, [*error userInfo], [*error localizedDescription]);
}
return persistentCoordinator;
}
I call it in
- (void) initCoreDataProperties
{
NSError *error;
// Creating the Managed Object Model from momd
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:TBCoreDataModelFileName withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
// Creating the Encrypted Store Persistent Coordinator
_persistentStoreCoordinator = [EncryptedStore makeStoreWithOptions: [self persistentStoreOptions]
managedObjectModel: self.managedObjectModel
error: &error];
First, do not check the error for an error state. Only check the return of the call to -addPersistentStoreWithType.... The error can be populated even in a non-error condition.
Your code looks fine so I suspect if you turned off the encrypted store and used an Apple provided SQLite store then it would work fine. Which means the issue is with that third party code.
Since the third party code is not providing you with an error or a NSPersistentStore then it is failing poorly and you need to open a bug against the code base so that the author can address it.
Or you can walk through that third part code and see where it is failing and why.

Coredata database prepopulation is not filled

I'm trying to prepopulate a coredata database from a sqlite file, but I don't understand why the database is not filled. My sqlite file is properly filled with all the data I need and added in the bundle ressources.
Here the persistentStoreCoordinator method int the AppDelegate :
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (persistentStoreCoordinator != nil)
{
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingString:#"MyApp.db"];
NSError *error = nil;
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:storePath])
{
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"MyApp" ofType:#"db"];
if (defaultStorePath)
{
[fm copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, 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();
}
return persistentStoreCoordinator;
}
In the view controller, I try to fetch the entries but it seems enter code hereempty :
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
initWithEntityName:#"Type_textimage_4text"];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *description = [NSEntityDescription entityForName:#"Type_textimage_4text" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:description];
NSMutableArray *questions = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSLog(#"question : %#", questions);
I probably missed something but I don't find what. Anyone can help me please ?
In answer to the coment above, I'm adding the AppDelegate.m file of one App which includes a prepopulated sqlite database. To do that I selected "Use Core Data" when I created the project on xcode. Although it works for me, I believe it is now a better praxis to use NSDocument for this kind of applications. One of the advantages is that the project is already on a good track to be iCloud compatible. I haven't done that yet, so I'm speaking only from what I've read/seen.
//
// AppDelegate.m
//
//
//
#import "AppDelegate.h"
#import "MainView.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UINavigationController *navViewController = (UINavigationController *)self.window.rootViewController;
MainView *main=[[navViewController viewControllers]objectAtIndex:0];
main.managedObjectContext=self.managedObjectContext;
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
#pragma mark - Core Data stack
- (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:#"Solvents" withExtension:#"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
- (NSString *)applicationDocumentsDirectoryString {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil) {
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Solvents.sqlite"];
NSString *storePath = [[self applicationDocumentsDirectoryString] stringByAppendingPathComponent: #"Solvents.sqlite"];
//NSLog(#"store path %#", storePath);
//NSLog(#"store URL%#", storeURL );
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath ]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"Solvents" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
#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

Core Data populated in Simulator but not moving to Device

I've found lots of explanations and help here on stack but so far, no luck.
Pretty much, my myapp.sqlite (that I pre-populated) works fine on the simulator but when I run it on the iPad, it's empty.
So after trying different things, this is the closest I got :
I copy the sqlite db into the Bundle but I move it to the Library folder.
On my AppDelegate I do this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"Starting to save the DB to another location");
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString *targetPath = [libraryPath stringByAppendingPathComponent:#"myDB.sqlite"];
if (![[NSFileManager defaultManager] fileExistsAtPath:targetPath]) {
// database doesn't exist in your library path... copy it from the bundle
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"myDB" ofType:#"sqlite"];
NSError *error = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:targetPath error:&error]) {
NSLog(#"Error: %#", error);
}
}
return YES;
}
then on the PersistentStoreCoordinator, I do this
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString *targetPath = [libraryPath stringByAppendingPathComponent:#"myDB.sqlite"];
// NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"naccApp.sqlite"];
// NSURL *storeURLLocal = [[NSBundle mainBundle] URLForResource:#"myDB" withExtension:#"sqlite"];
NSURL *storeURL = [NSURL URLWithString:targetPath];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
When I try to use the DB, (fetchRequest), I get an error :
CoreData SQL stores only support file URLs (got /var/mobile/Applications/2EB2AADD-DF9D-475F-A05E-BB138502471F/Library/myDB.sqlite).
The message is clear but I've tried almost all the help here and still nothing.
Needless to say, I'm new to Core Data, so please forgive the ignorance.
Oh, I'm using xCode 5.
Thx
NSURL *storeURL = [NSURL URLWithString:targetPath];
Should be:
NSURL *storeURL = [NSURL fileURLWithPath:targetPath];
so that you generate a file URL instead of a web URL.

Error message in Core Data

I've been using Core Data in my app, and suddenly I got en error message as follows:
Attempt to add read-only file at path file://localhost/var/mobile/Applications/xxx-xxx-xxx../.app/MyModel.sqlite read/write. Adding it read-only instead. This will be a hard error in the future; you must specify the NSReadOnlyPersistentStoreOption.
The error occurred in the method below:
-(NSPersistentStoreCoordinator*)persistentStoreCoordinator{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSString* path= [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"MyModel.sqlite"];
NSURL* storeURL = [[NSURL alloc] initFileURLWithPath:path];
NSError* error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
Before today, I have never seen this error message before. Can any one tell me what went wrong? Thank you for your time.
The answer lies in
[[NSBundle mainBundle] resourcePath]
The mainBundle resourcePath is where to load data contained in your application IPA file, it is and will always be readonly, you need to create the sqllite file in the application document folder
you can replace it by
NSURL *storeURL = [[self applicationPrivateDocumentFolderURL] URLByAppendingPathComponent:#"MyModel.sqlite"];
- (NSURL *)applicationPrivateDocumentFolderURL
{
if (!m_privateDocumentFolderURL) {
NSString *applicationPrivateDocumentFolderPath = [[NSFileManager defaultManager] applicationSupportDirectory];
m_privateDocumentFolderURL = [[NSURL alloc] initFileURLWithPath:applicationPrivateDocumentFolderPath];
}
return [[m_privateDocumentFolderURL copy] autorelease];
}
For anyone landing here because they're trying to ship a read-only database with their app, here's the code to create the store
NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption: #(true),
NSInferMappingModelAutomaticallyOption: #(true),
NSReadOnlyPersistentStoreOption: #(true))
// The NSReadOnlyPersistentStoreOption is the important one here
NSPersistentStore * seedStore =[coordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:#"Seed"
URL:[NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:#"Seed" ofType:#".sqlite"]]
options:options
error:&error];
Also, when you create your database, you'll probably have your journal mode set to WAL, which is not compatible with a read-only db. You have 2 choices:
add NSSQLitePragmasOption : #{#"journal_mode" : #"DELETE"}} to your options
or open the seed database with a tool such as Liya and run "PRAGMA wal_checkpoint(RESTART)" followed by "PRAGMA journal_mode = DELETE

Resources