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
Related
I am newbie in Core Data. I put all model requirements (in respect of MVC) in a class of NSFetchedResultsController as below:
Header File :
#import <CoreData/CoreData.h>
#interface GeneralModel : NSFetchedResultsController
#property (nonatomic, strong) NSManagedObjectContext *context;
#property (nonatomic, strong) NSManagedObjectModel *model;
- (NSString *)storagePath;
- (void)removeStorage;
- (void)truncateAllEntity;
- (void)truncateEntity:(NSString *)entityName;
- (void)addGroups:(NSDictionary *)insertData;
- (NSArray *)getEntity:(NSString *)entityName sortBy:(NSString *)sortAttribute;
- (id)getMaxValue:(NSString *)entityName forProperty:(NSString *)propertyName;
- (NSArray *)getEntity:(NSString *)entityName predicateBy:(NSPredicate *)predicate sortBy:( NSString * )sortAttribute;
#end
Main File:
#import "GeneralModel.h"
#import "GeneralHelper.h"
#implementation GeneralModel
- (instancetype)init
{
self = [super init];
if (self) {
// Read in Model.xcdatamodeld
_model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc =
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_model];
// Where does the SQLite file go?
NSString *path = self.storagePath;
NSURL *storeURL = [NSURL fileURLWithPath:path];
NSError *error = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:nil
error:&error]) {
#throw [NSException exceptionWithName:#"OpenFailure"
reason:[error localizedDescription]
userInfo:nil];
}
// Create the managed object context
_context = [[NSManagedObjectContext alloc] init];
_context.persistentStoreCoordinator = psc;
}
return self;
}
- (NSString *)storagePath
{
NSArray *documentDirectories =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
// Get one and only document directory from that list
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:#"model.sqlite"];
}
- (void)removeStorage {
// NSError *error2;
// NSString *storagePath = [self storagePath];
//
// NSDictionary *options = #{NSPersistentStoreUbiquitousContentNameKey: #"model"};
// bool removeResult = [NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:[NSURL URLWithString:storagePath] options:options error:&error2];
// if (removeResult == NO) {
// NSLog(#"Could not remove Storage. Reason: %#", error2.localizedFailureReason);
// }
NSPersistentStore *store = [self.context.persistentStoreCoordinator.persistentStores lastObject];
NSError *error = nil;
NSURL *storeURL = store.URL;
BOOL isRemovePersistentStore = [self.context.persistentStoreCoordinator removePersistentStore:store error:&error];
if (isRemovePersistentStore == NO) {
NSLog(#"NO RemovePersistentStore. Reason: %#", error.localizedFailureReason);
}
BOOL isRemoveItemAtURL = [[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
if (isRemoveItemAtURL == NO) {
NSLog(#"NO RemoveItemAtURL. Reason: %#", error.localizedFailureReason);
}
}
- (void)truncateAllEntity {
NSArray *entities = self.model.entities;
for (NSEntityDescription *entityDescription in entities) {
[self truncateEntity:entityDescription.name];
}
}
- (void)truncateEntity:(NSString *)entityName {
// delete all database
if (IOS_VERSION >= 9) {
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:entityName];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
NSError *deleteError = nil;
[self.context.persistentStoreCoordinator executeRequest:delete withContext:self.context error:&deleteError];
} else {
NSFetchRequest *allItems = [[NSFetchRequest alloc] init];
[allItems setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self.context]];
[allItems setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError *error = nil;
NSArray *items = [self.context executeFetchRequest:allItems error:&error];
//error handling goes here
for (NSManagedObject *item in items) {
[self.context deleteObject:item];
}
NSError *saveError = nil;
[self.context save:&saveError];
//more error handling here
}
[self.context rollback];
}
- (void)addGroups:(NSDictionary *)insertData {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
for (NSDictionary *response in insertData) {
NSManagedObject *existingGroup = [self getEntity:#"Group" AtValue:response[#"groupId"] forProperty:#"group_id" ];
if (existingGroup) {
NSLog(#"existingGroup");
continue;
}
// for (int i; i<=20; i++) {
// set bullet
NSManagedObject *object_bullet = [NSEntityDescription insertNewObjectForEntityForName:#"Group_bullet"
inManagedObjectContext:self.context];
NSData *bulletData = [[NSData alloc] initWithBase64EncodedString:response[#"bullet"] options:NSDataBase64DecodingIgnoreUnknownCharacters];
[object_bullet setValue:bulletData forKey:#"bullet"];
[object_bullet setValue:response[#"groupId"] forKey:#"group_id"];
// set group
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"Group"
inManagedObjectContext:self.context];
[object setValue:object_bullet forKey:#"relatedBullet"];
// NSDate *createDate = [dateFormatter dateFromString:response[#"createDate"]];
// [object setValue:createDate forKey:#"group_created_date"];
NSNumber *createDate = response[#"createDate"];
[object setValue:[NSDate dateWithTimeIntervalSince1970:[createDate doubleValue]] forKey:#"group_created_date"];
[object setValue:response[#"groupId"] forKey:#"group_id"];
[object setValue:[GeneralHelper getInteger:response[#"memberNumber"]] forKey:#"group_members_count"];
[object setValue:response[#"name"] forKey:#"group_name"];
[object setValue:[GeneralHelper getInteger:response[#"privacy"]] forKey:#"group_privacy"];
[object setValue:#1 forKey:#"group_status"];
[object setValue:response[#"imageName"] forKey:#"image_name"];
[object setValue:response[#"lastMessageId"] forKey:#"last_message_id"];
[object setValue:response[#"lastMessageText"] forKey:#"last_message_text"];
//
// NSDate *lastMessageDate = [dateFormatter dateFromString:response[#"lastMessageDate"]];
// [object setValue:lastMessageDate forKey:#"last_time"];
NSNumber *lastMessageDate = response[#"lastMessageDate"];
[object setValue:[NSDate dateWithTimeIntervalSince1970:[lastMessageDate doubleValue]] forKey:#"last_time"];
[object setValue:response[#"machineId"] forKey:#"machine_id"];
NSNumber *timestamp = response[#"timestamp"];
[object setValue:[NSDate dateWithTimeIntervalSince1970:[timestamp doubleValue]] forKey:#"timestamp"];
[object setValue:[GeneralHelper getInteger:response[#"unreadCount"]] forKey:#"unread_count"];
// }
}
NSError *errorInsert;
if (![self.context save:&errorInsert]) {
NSLog(#"Failed to save - error: %#", [errorInsert localizedDescription]);
}
[self.context rollback];
}
- (id)getMaxValue:(NSString *)entityName forProperty:(NSString *)propertyName {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:entityName];
fetchRequest.fetchLimit = 1;
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:propertyName ascending:NO]];
NSError *error = nil;
id maxValue = [self.context executeFetchRequest:fetchRequest error:&error].firstObject;
if (maxValue == nil) {
maxValue = #{propertyName : #0};
}
[self.context rollback];
return [maxValue valueForKey:propertyName];
}
- (id)getEntity:(NSString *)entityName AtValue:(NSString *)indexValue forProperty:(NSString *)propertyName {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self.context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", propertyName, indexValue];
// NSLog(#"predicate: %#", predicate);
[request setEntity:entity];
[request setPredicate:predicate];
[request setFetchLimit:1];
NSError *error;
NSArray *results = [self.context executeFetchRequest:request error:&error];
NSManagedObject *object = [results firstObject];
// if (object) {
// // there is at least one object matching you predicate
// } else {
// // there is no object matching your predicate in the moc
// }
// NSLog(#"results member %#", results);
// [self.context rollback];
return object;
}
Then I use this general model in my UITableViewController.
Is there any better way to do this? Or my implementation is buggy and useless?
It think this is a nice approach, but it has some drawbacks.
An NSFetchedResultsController is responsible, as the name implies for controlling results that were fetched. If you add functionality like deleting and modifying things, you are making an object with too many responsibilities.
Imagine there is one viewController showing results in a tableview. It would only need the fetchedresultscontroller functionality, nothing more. If by tapping on a cell you go into a new view controller where edits are possible, you don't need a fetchedresultscontroller: you need a managedObject to edit. This editViewController could use a child context of the tableViewController so that on cancel, all edits are discarded by simply discarding the child context.
I would suggest to no subclass the NSFetchedResultsController, but instead implement a subclass of NSObject that you initialize with an NSNMagedObjectContext that can do the things that apply to the entire context. Like adding groups, removing all data etc.
Then use composition: this NSObject subclass can be a property of a viewController so the viewCOntroller has a way to do those things without having this operation being specific to this viewController.
In general, instead of subclassing, consider composition first. This is because transforming a small components of a composed object to an object that is a certain subclass is usually easier then the other way around.
Feel free to disagree :), but this is what I've learned from experience of working with many people for a long time on a very large codebase.
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 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];
}];
I'm creating an object with the following code:
+(Checkin *) newCheckinWithId:(NSString*) checkinID forVenueId:(NSString *)venueId
{
NSManagedObjectContext * context = [[AppDelegate sharedInstance] managedObjectContext];
Checkin *ret = (Checkin *)[NSEntityDescription insertNewObjectForEntityForName:#"Checkin" inManagedObjectContext:context];
ret.checkinID = checkinID;
ret.forVenueID = venueId;
ret.date = [NSDate date];
NSError * error;
if(![context save:&error]) {
NSLog(#"Error saving!!!!!: %#", error.userInfo);
}
return ret;
}
This code works, and I can see the objects in the sqldatabase file on disk (in ~/library/iphone sim/.. whatever it is)
Here is the code I use to create my store + managed object context (I use 1 context for everything) Its basically all from a stack overflow post I found
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil)
{
return _managedObjectModel;
}
//NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model" withExtension:#"momd"];
//__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//NSArray *testArray = [[NSBundle mainBundle] URLsForResourcesWithExtension:#"momd"subdirectory:nil];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Model" ofType:#"momd"];
if( !path ) path = [[NSBundle mainBundle] pathForResource:#"Model" ofType:#"mom"];
NSURL *modelURL = [NSURL fileURLWithPath:path];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//__managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if((_persistentStoreCoordinator != nil)) {
return _persistentStoreCoordinator;
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSPersistentStoreCoordinator *psc = _persistentStoreCoordinator;
// Set up iCloud in another thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ** Note: if you adapt this code for your own use, you MUST change this variable:
NSString *iCloudEnabledAppID = #"[MY APP ID]";
// ** Note: if you adapt this code for your own use, you should change this variable:
NSString *dataFileName = #"foursquareaugmentation.sqlite";
// ** Note: For basic usage you shouldn't need to change anything else
NSString *iCloudDataDirectoryName = #"Data.nosync";
NSString *iCloudLogsDirectoryName = #"Logs";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];
if (iCloud) {
NSLog(#"iCloud is working");
NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];
NSLog(#"iCloudEnabledAppID = %#",iCloudEnabledAppID);
NSLog(#"dataFileName = %#", dataFileName);
NSLog(#"iCloudDataDirectoryName = %#", iCloudDataDirectoryName);
NSLog(#"iCloudLogsDirectoryName = %#", iCloudLogsDirectoryName);
NSLog(#"iCloud = %#", iCloud);
NSLog(#"iCloudLogsPath = %#", iCloudLogsPath);
if([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if(fileSystemError != nil) {
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
NSString *iCloudData = [[[iCloud path]
stringByAppendingPathComponent:iCloudDataDirectoryName]
stringByAppendingPathComponent:dataFileName];
NSLog(#"iCloudData = %#", iCloudData);
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[options setObject:iCloudEnabledAppID forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:iCloudLogsPath forKey:NSPersistentStoreUbiquitousContentURLKey];
[psc lock];
NSError *error;
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[NSURL fileURLWithPath:iCloudData]
options:options
error:&error];
if( error )
{
NSLog(#"Error adding persistent store %#, %#", error, [error userInfo]);
// comment in this line while debugging if get "Can't find model for source store" error in addPersistentStoreWithType.
// it means the sqlite database doesn't match the new model and needs to be created from scratch.
// this happens if you change the xcdatamodel instead of creating a new one under Xcode->Editor->Add Model Version...
// CoreData can only automatically migrate if there is a new model version (it can't migrate if the model simply changes, because it can't see the difference between the two models).
// be sure to back up the database if needed, because all data will be lost.
//[fileManager removeItemAtPath:iCloudData error:&error];
/*// this is another way to verify the hashes for the database's model to make sure they match one of the entries in the momd directory's VersionInfo.plist
NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:[NSURL fileURLWithPath:iCloudData] error:&error];
if( !sourceMetadata )
NSLog(#"sourceMetadata is nil");
else
NSLog(#"sourceMetadata is %#", sourceMetadata);*/
}
[psc unlock];
}
else {
NSLog(#"iCloud is NOT working - using a local store");
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[psc lock];
NSError *error;
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:localStore
options:options
error:nil];
if( error )
NSLog(#"Error adding persistent store %#, %#", error, [error userInfo]);
[psc unlock];
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"SomethingChanged" object:self userInfo:nil];
});
});
return _persistentStoreCoordinator;
}
/*
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *) managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return _managedObjectContext;
}
Here is my fetch:
+(NSArray *) checkinsForVenue:(NSString *) venueID
{
NSManagedObjectContext * context = [[AppDelegate sharedInstance] managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Checkin" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
//NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(forVenueID = %#)", venueID];
//request.predicate = predicate;
NSError *error;
NSArray * ret = [context executeFetchRequest:request error:&error];
if(ret == nil) {
NSLog(#"%#", error.description);
}
return ret;
}
Fetch always returns no objects (the empty array) even though there are things in the database!
I've been working on this problem for quiet a while and am pretty out of ideas so anything appreciated!
Thanks
Try this code to fetch data:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil )
{
return _fetchedResultsController;
}
NSLog(#"Length:: %d",[[_fetchedResultsController mutableCopy] length]);
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"OrderDetails"];
[fetchRequest setIncludesSubentities:YES];
// Sort by Application name
NSSortDescriptor* sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"orderId" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
//Sort by CommandNameloca
NSArray* sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor1, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:100];
ordersArr=[[NSMutableArray alloc]initWithArray:sortDescriptors];
NSLog(#"----- ordersArr Count == %d",[ordersArr count]);
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"orderId" cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&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();
}
return _fetchedResultsController;
}
After this method i executed write this line to store data in your app array like this:
NSMutableArray *myArr=[[NSMutableArray alloc]initWithArray:self.fetchedResultsController.fetchedObjects];
My method checkIfExistDatabase must returns YES for a second run, but it returns NO. That's a problem.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[CoreDataWrapper sharedInstance] checkIfExistDatabase];
return YES;
}
-(void) checkIfExistDatabase
{
NSURL *url = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Mew.sqlite"];
BOOL isExists = [[NSFileManager defaultManager] fileExistsAtPath:[url absoluteString]];
if (!isExists)
{
[self populateDatabase];
}
}
-(void) populateDatabase
{
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Test" inManagedObjectContext:[self managedObjectContext]];
for (int i =0; i<2; i++)
{
Test *test = [[[Test alloc] initWithEntity:entity insertIntoManagedObjectContext:[self managedObjectContext]] autorelease];
test.text = #"Text";
test.index = [NSNumber numberWithInt:i];
}
[self saveContext];
}
UPDATE: I added applicationDocumentsDirectory method
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
and output:
NSLog(#"absolute string: %#", [url absoluteString]);
absolute string: file://localhost/var/mobile/Applications/6EE4D791-B798-4A2E-83DD-BFA41ED86940/Documents/Mew.sqlite
Don't check for existence of the entity, check for objects (records) in that entity. Something like this:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Test" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [self.managedObjectContext countForFetchRequest:request error:&error];
If you have a count greater than 0, you have a populated core model entity.
Doesn't fileExistsAtPath: take a path rather than a URL?
NSString *file = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"Mew.sqlite"];
BOOL isExists = [[NSFileManager defaultManager] fileExistsAtPath:file];