I am trying to understand how core data works.
So I have in my core data 2 entities : Voiture and Garage (yes, i'm french :) )
I can create objects but i can't delete them ! I tried everything…
It would be nice to help me a bit !
here is my code :
#interface dataBaseViewController ()
#property(strong,nonatomic) UIManagedDocument *document;
#property(strong,nonatomic) NSManagedObjectContext *context;
#end
#implementation dataBaseViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a
[self initDocument];
self.context=self.document.managedObjectContext;
}
-(void) initDocument{
//find url
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory=[[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSString *documentName=#"MyDocument";
NSURL *url= [documentsDirectory URLByAppendingPathComponent:documentName];
//create / open the document
self.document = [[UIManagedDocument alloc] initWithFileURL:url] ;
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[self.document openWithCompletionHandler:^(BOOL success) {
if (success) NSLog(#"doc ouvert");
if (!success) NSLog(#"couldn’t open document at %#", url);
}];
} else {
[self.document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) NSLog(#"document created");
if (!success) NSLog(#"couldn’t create document at %#", url);
}];
}
}
- (IBAction)ajouterVoiture:(id)sender {
Voiture *nouvelleVoiture =[NSEntityDescription insertNewObjectForEntityForName:#"Voiture" inManagedObjectContext:self.context];
nouvelleVoiture.marque=#"ferreri";
}
- (IBAction)nbVoitures:(id)sender {
NSError *error;
NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:#"Voiture"];
NSLog(#"nombre de voitures : %lu",[self.context countForFetchRequest:request error:&error]);
}
- (IBAction)delete:(id)sender {
[self.context deletedObjects];
NSError *error;
[self.context save:&error];
}
#end
Once you have fetched a managed object, you can delete it from its managed object context by using the deleteObject: method provided by the context.
NSManagedObject *someObject;
[context deleteObject:someObject];
The object will not be removed from the underlying persistent store on disk until the context is saved, using the save: method.
I highly recommend the Magical Record pod. It greatly simplifies boilerplate Core Data operations.
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) {
MyEntity *entity = [MyEntity MR_createInContext:localContext];
MyOtherEntity *otherEntity = [MyOtherEntity MR_findFirstByAttribute:#"id" withValue:12345];
entity.attribute1 = #"Foo";
entity.attribute2 = #"Bar";
[otherEntity deleteInContext:localContext];
}];
Related
ANSWERED Q2 by FIXING Q1, FEEL FREE TO EXPLAIN IF YOU KNOW WHY
Full disclosure, I dabble in Objective C so this could be painfully obvious but I could not find similar problems or more accurately answers to similar problems.
I'm writing a very simple app and I am learning about core data and I have a couple of issues
Q.1 Has been answered so here is Q.2
Q2. When fetching from Core Data I retrieve empty objects?
With the Data Controller being the same as the above question I call
- (NSMutableArray*) returnTimers
{
[self initilizeDataLayer];
NSMutableArray *listOfTimers = [[NSMutableArray alloc]init];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"Timer"];
NSError *error = nil;
NSArray *listOfTimersRaw = [myDataController.managedObjectContext executeFetchRequest:request error:&error];
if (error != nil) {
//Deal with failure
}
else {
//Deal with success
listOfTimers = [NSMutableArray arrayWithArray:listOfTimersRaw];
}
return listOfTimers;
}
This correctly retrieves 2 objects but they are empty?
Again this is experimentation so may be arse ways but I have to learn somehow.....Any help would be greatly appreciated.
BELOW HERE HAS BEEN ANSWERED THANKS
Q1. Saving Objects to Core Data creates a blank row in sqlite db - Why?
I have set up a DataController Object like in the Apple Docs
- (void)initializeCoreData
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"WorkRestWork" withExtension:#"momd"];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSAssert(mom != nil, #"Error initializing Managed Object Model");
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc setPersistentStoreCoordinator:psc];
[self setManagedObjectContext:moc];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsURL URLByAppendingPathComponent:#"WorkRestWork.sqlite"];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:_options error:&error];
NSAssert(store != nil, #"Error initializing PSC: %#\n%#", [error localizedDescription], [error userInfo]);
});
}
Then I call this method
- (BOOL) addTimerWithName: (NSString*)timerName
{
[self initilizeDataLayer];
Timer *newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
newTimer.name = timerName;
[myDataController.managedObjectContext insertObject:newTimer];
NSError *error = nil;
if ([myDataController.managedObjectContext save:&error] == NO) {
NSAssert(NO, #"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
return NO;
}
return YES;
}
And Finally
[cdh addTimerWithName:#"A TEST"];
When I check check the .sqlite file using Base there are 2 rows, one with "A TEST" as the Name and the other empty which is confusing me
Ad Q1.
You are inserting two timers here. However you assign a name just to the second one. First one has no name cause you never set it. Here's your code with some comments:
// Inserts first timer
Timer *newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
// Inserts second timer
newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
// Sets name of the second timer
newTimer.name = timerName;
I changed my existing core stack and my sqlite file doesn't get updated anymore after it's created. I'm able to save and fetch data with core data, but the sqlite file is always empty(the sqlite-shm file gets updated instead). Is my implementation wrong? What do I need to do to get my sqlite file to be updated?
//header file
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectContext *masterManagedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectContext *mainManagedObjectContext;
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator;
-(NSManagedObjectModel *)managedObjectModel;
-(NSManagedObjectContext *)masterManagedObjectContext;
-(NSManagedObjectContext *)mainManagedObjectContext;
-(NSManagedObjectContext *)workerManagedObjectContext;
//implementation file
#synthesize managedObjectModel = __managedObjectModel;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
#synthesize masterManagedObjectContext = __masterManagedObjectContext;
#synthesize mainManagedObjectContext = __mainManagedObjectContext;
//Managed object context
-(NSManagedObjectContext *)masterManagedObjectContext{
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[__masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __masterManagedObjectContext;
}
- (NSManagedObjectContext *)mainManagedObjectContext {
if (__mainManagedObjectContext != nil) {
return __mainManagedObjectContext;
}
__mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__mainManagedObjectContext setParentContext:self.masterManagedObjectContext];
return __mainManagedObjectContext;
}
- (NSManagedObjectContext *)workerManagedObjectContext {
NSManagedObjectContext *tempMOContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tempMOContext.parentContext = [self mainManagedObjectContext];
return tempMOContext;
}
//Save methods
- (void)saveWorkerContext:(NSManagedObjectContext *)context {
NSError *error;
[context save:&error];
if (!error) {
[self saveMainContext];
}
}
- (void)saveMainContext {
[self.mainManagedObjectContext performBlock:^{
NSError *error = nil;
[self.mainManagedObjectContext save:&error];
if(!error){
//Write to disk after saving on the main UI context
[self saveMasterContext];
}
}];
}
- (void)saveMasterContext {
[self.masterManagedObjectContext performBlock:^{
NSError *error = nil;
[self.masterManagedObjectContext save:&error];
if(error){
NSLog(#"CORE DATA MASTER CONTEXT ERROR : %#", error);
}
}];
}
-(NSManagedObjectModel *)managedObjectModel {
if (__managedObjectModel != nil) {
return __managedObjectModel;
}
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"MyResource"
ofType:#"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *modelPath = [bundle pathForResource:#"MyData"
ofType:#"momd"];
//NSLog(#"Bundle modelURL:%#",modelPath);
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (__persistentStoreCoordinator != nil) {
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"MyData.sqlite"];
//NSLog(#"storeURL:%#",storeURL);
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;
}
- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
//example of trying to save data
-(void)saveDictionary:(NSMutableDictionary *)myDictionary{
NSManagedObjectContext *context = [self workerManagedObjectContext];
[context performBlockAndWait:^{
NSMutableDictionary *tmpDic = myDictionary;
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
Entity *myEntity;
if ([fetchedObjects count]>0) {
myEntity = [fetchedObjects lastObject];
}
else{
myEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:context];
}
myEntity.value = tmpDic[#"value1"]!=nil?tmpDic[#"value1"]:#"nothing";
[self saveWorkerContext:context];
}];
}
What do I need to do to get this to work?
But you also said
I'm able to save and fetch data with core data...
That's the definition of "working" with Core Data. If that works, you're done.
The SQLite shm file is the SQLite journal. It's an implementation detail of how SQLite works. You can read up on how SQLite manages its files if you're interested but it's not really relevant to what you're doing.
Simply for the time you want to generate sqlite file use the following options when you add persistent store:
#{NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}}
[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
URL:self.localStoreURL
options:#{NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}}
error:nil];
Then run the application, quit, go to the application folder and in Documents your file will be updated. I hope this will help you.
This is My code:
This is iCloud with Coredata synchronization configuration code:
#pragma mark - Core Data stack
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.wanglichen.iPassword" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"iPassword" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// create a new persistent store of the appropriate type
NSError *error = nil;
NSURL *storeURL = [self applicationDocumentsDirectory];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
// ** Note: if you adapt this code for your own use, you MUST change this variable.
// is the full App ID (including the Team Prefix). You will need to change this to match the Team Prefix found in your own iOS Provisioning Portal.
NSString *iCloudEnabledAppID = [[NSBundle mainBundle] infoDictionary][#"CFBundleIdentifier"];
// the name of the SQLite database store file.
NSString *dataFileName = #"iPassword.sqlite";
// ** Note: For basic usage you shouldn't need to change anything else
// dataDirectory is the name of the directory the database will be stored in. It should always end with .nosync
// iCloudData = iCloudRootPath + dataDirectory
NSString *iCloudDataDirectoryName = #"Data.nosync";
// logsDirectory is the name of the directory the database change logs will be stored in.
NSString *iCloudLogsDirectoryName = #"Logs";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localStore = [storeURL URLByAppendingPathComponent:dataFileName];
// iCloudRootPath is the URL to your apps iCloud root path.
NSURL *iCloudRootPath = [fileManager URLForUbiquityContainerIdentifier:nil];
if (iCloudRootPath) // If iCloud is working, save it to iCloud container.
{
NSLog(#"iCloud is working.");
// Place core data sqlite file in iCloudRootPath/Data.nosync/ (The subdirectory should be ended with .nosync)
// Place the log file in iCloudRootPath/Logs (All changed in iCloud will be download to log file firstly.)
NSURL *iCloudLogsPath = [iCloudRootPath URLByAppendingPathComponent:iCloudLogsDirectoryName];
// NSLog(#"iCloudEnabledAppID = %#",iCloudEnabledAppID);
// NSLog(#"dataFileName = %#", dataFileName);
// NSLog(#"iCloudDataDirectoryName = %#", iCloudDataDirectoryName);
// NSLog(#"iCloudLogsDirectoryName = %#", iCloudLogsDirectoryName);
// NSLog(#"iCloud = %#", iCloudRootPath);
// NSLog(#"iCloudLogsPath = %#", iCloudLogsPath);
NSURL *iCloudDataURL = [iCloudRootPath URLByAppendingPathComponent:iCloudDataDirectoryName];
if ([fileManager fileExistsAtPath:[iCloudDataURL path]] == NO)
{
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[iCloudDataURL path]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if(fileSystemError != nil)
{
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
iCloudDataURL = [iCloudDataURL URLByAppendingPathComponent:dataFileName];
// NSLog(#"iCloudDataPath = %#", iCloudDataURL);
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:#(YES) forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:#(YES) forKey:NSInferMappingModelAutomaticallyOption];
[options setObject:iCloudEnabledAppID forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:iCloudLogsPath forKey:NSPersistentStoreUbiquitousContentURLKey];
[_persistentStoreCoordinator lock];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:iCloudDataURL
options:options
error:nil])
{
NSDictionary *ui = [error userInfo];
for(NSString *err in [ui keyEnumerator]) {
NSLog(#"err:%#",[ui objectForKey:err]);
}
abort();
}
[_persistentStoreCoordinator unlock];
}
else // If iCloud is not working, save it to local.
{
NSLog(#"iCloud is NOT working - using a local store");
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:#(YES) forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:#(YES) forKey:NSInferMappingModelAutomaticallyOption];
[options setObject:#(YES) forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:#(YES) forKey:NSInferMappingModelAutomaticallyOption];
[_persistentStoreCoordinator lock];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:localStore
options:options
error:nil])
{
NSDictionary *ui = [error userInfo];
for(NSString *err in [ui keyEnumerator]) {
NSLog(#"err:%#",[ui objectForKey:err]);
}
abort();
}
[_persistentStoreCoordinator unlock];
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
// Register NSPersistentStoreDidImportUbiquitousContentChangesNotification, so that
// coreDataChangedIniCloud will be called if core data in iCloud is changed.
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(coreDataChangedIniCloud:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:self.persistentStoreCoordinator];
return _managedObjectContext;
}
- (void)coreDataChangedIniCloud:(NSNotification *)notification
{
NSLog(#"Merging in changes from iCloud...");
[self.managedObjectContext performBlock:^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
NSLog(#"new data from iCloud: %#", notification.object);
[[NSNotificationCenter defaultCenter] postNotificationName:#"MergingInChangesFromICloud" object:notification.object userInfo:[notification userInfo]];
}];
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = 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();
}
}
}
Here is the problem I encountered:
Crash mark
When iCloud data inside the change, I call the following method:
- (void)coreDataChangedIniCloud:(NSNotification *)notification
{
NSLog(#"Merging in changes from iCloud...");
[self.managedObjectContext performBlock:^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
NSLog(#"new data from iCloud: %#", notification.object);
[[NSNotificationCenter defaultCenter] postNotificationName:#"MergingInChangesFromICloud" object:notification.object userInfo:[notification userInfo]];
}];
}
This is the cause of the crash:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can only use -performBlock: on an NSManagedObjectContext that was created with a queue.
performBlock (and performBlockAndWait) can only be used for NSManagedObjectContexts that were initialised with either NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType. By default NSManagedObjectContexts are initialised to use the NSConfinementConcurrencyType which does not support performBlock or performBlockAndWait.
You should change this line:
_managedObjectContext = [[NSManagedObjectContext alloc] init];
to either:
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
or
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
Try this.
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
- (void)coreDataChangedIniCloud:(NSNotification *)notification
NSManagedObjectContext *moc = self.managedObjectContext;
[moc performBlockAndWait:^{
[moc mergeChangesFromContextDidSaveNotification:notification];
}];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"MergingInChangesFromICloud" object:notification.object userInfo:[notification userInfo]];
});
}
I come across a problem, every time I try to get the SQLite file using documentDirectory, I just could not find it. I really wonder where the SQLite file is put in the project file and how I can get the name of it so that I can find the SQLite file.
- (void) save
{
// Create UIManagedDocument
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectory = [[fileManager URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask]firstObject];
NSString *documentName = #"Model";
NSURL *url = [documentDirectory URLByAppendingPathComponent:documentName];
UIManagedDocument *document = [[UIManagedDocument alloc]initWithFileURL:url];
if ([fileManager fileExistsAtPath:[url path]]) {
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
if (document.documentState == UIDocumentStateNormal) {
// Get a ManagedObjectContext
NSManagedObjectContext *context = document.managedObjectContext;
// Set managed object (entity)
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attribute (which are "name" and "age") of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check whether there is an error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
// Close the window
[self dismissViewControllerAnimated:YES completion:nil];
}
}
if (!success) {
NSLog(#"couldn't open document at %#", url);
}
}];
}
else {
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) {
if (document.documentState == UIDocumentStateNormal) {
// Get a ManagedObjectContext
NSManagedObjectContext *context = document.managedObjectContext;
// Set managed object (entity)
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attribute (which are "name" and "age") of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check whether there is an error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
// Close the window
[self dismissViewControllerAnimated:YES completion:nil];
}
}
if (!success) {
NSLog(#"couldn't open document at %#", url);
}
}];
}
}
Every time when I run the apps, the debugger will say that:
2014-07-29 15:41:22.476 TableAndCoreData[2502:60b] couldn't open document at file:///Users/Mike/Library/Application%20Support/iPhone%20Simulator/7.1/Applications/DB1F0215-2BF2-43B5-ADF2-76ABC9E2CD16/Library/Documentation/Model
try this instead:
NSURL *documentDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]firstObject];
I am trying to implement Core Data in an iOS 7 app (and have successfully done so earlier). However, when I execute the executeFetchRequest method, the app crashes. The most relevant code is added below:
#import "JTHuntingSeasonDB.h"
#implementation JTHuntingSeasonDB
- (id)init {
self = [super init];
if (self) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSString *documentName = #"JTHuntingDB";
NSURL *url = [documentsDirectory URLByAppendingPathComponent:documentName];
document = [[UIManagedDocument alloc] initWithFileURL:url];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];
if (fileExists) {
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(#"Document opened successfully");
self.allSpecies = [self getSpecies];
self.speciesToday = [self getSpeciesToday];
} else NSLog(#"Failed to open document");
}];
} else {
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) {
NSLog(#"Document created successfully");
self.allSpecies = [self getSpecies];
self.speciesToday = [self getSpeciesToday];
} else NSLog(#"Failed to create document, path: %#", url);
}];
}
}
return self;
}
#pragma mark - Core Data
- (NSArray *)getSpecies {
if (document.documentState == UIDocumentStateNormal) {
context = document.managedObjectContext;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *country = [defaults stringForKey:#"Country"];
NSString *subregion = [defaults stringForKey:#"Subregion"];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"HuntingDates"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(localizedStandardCompare:)];
request.fetchBatchSize = 150;
request.fetchLimit = 150;
request.sortDescriptors = #[sortDescriptor];
if (subregion.length > 0) {
request.predicate = [NSPredicate predicateWithFormat:#"(country IN %#) AND (subregion IN %#)", country, subregion];
} else {
request.predicate = [NSPredicate predicateWithFormat:#"country IN %#", country];
}
NSError *error;
return [context executeFetchRequest:request error:&error];
}
return nil;
}
The line
return [context executeFetchRequest:request error:&error];
causes the app to crash. This is what I have done and discovered so far:
Simulator: Deleted app and reset content and settings
Xcode: Clean (Build folder)
"context" is not nil
NSLog prints out "Document created/opened successfully"
Only "lldb" is printed in the console when the app crashes
Edit:
After following Wain's advice, I turned exception breakpoint off, got a more descriptive error message saying that the predicate was invalid. Problem solved by replacing IN in the predicate with CONTAINS and also changing the variable "country" so that it did not return nil anymore.
I guess it's your predicates, because IN is a collection operator but you're passing strings (should be array / set).
If you're using strings, you should be using CONTAINS in the predicate.
Assuming the CoreData persistent store lives inside the document, you will want to use a UIManagedDocument