There seem to be some modifications to the NSPersistentStoreCoordinator method is iOS 5.
I am trying to get a prepopulated database ... it doesn't seem to work, no crash but none of the data seems to exist... Any suggestions?
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storePath = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"DataModel.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:[storePath absoluteString]]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"DataModel" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:[storePath absoluteString] error:NULL];
}
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"DataModel.sqlite"];
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;
}
The problem is in the applicationDocumentsDirectory method that gives wrong absolute string. It is not really wrong, but is not suited for NSFileManager class.
The solution is simple. You need to write another method which I called applicationDocumentsDirectory2.
So first, go to header file of your AppDelegate and declare method:
- (NSString *)applicationDocumentsDirectory2;
Now, go to the .m file, and define a method:
- (NSString *)applicationDocumentsDirectory2 {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
And finally, just change persistentStoreCoordinator method into:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSError *error = nil;
NSString *storePath = [[self applicationDocumentsDirectory2] stringByAppendingPathComponent: #"DataModel.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"DataModel" ofType:#"sqlite"];
if (defaultStorePath) {
if(![fileManager copyItemAtPath:defaultStorePath toPath:storePath error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"DataModel.sqlite"];
__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;
}
That's it! :)
Related
I have an app that has been rejected 3 times due to icloud backup issues. Apple have written back to say that I need to use there bit of code to exclude the files from being backed up. However this isnt working and i am at wits end.
Here is the code i've used
- (BOOL)downloadFile:(NSString *)fileURI targetFolder:(NSString *)targetFolder targetFilename:(NSString *) targetFilename{
#try{
NSError *error = nil;
NSURL *url = [NSURL URLWithString:fileURI];
if(![url setResourceValue:#"YES" forKey:NSURLIsExcludedFromBackupKey error:&error]){
NSLog(#"KCDM: Error excluding %# from backup %#", fileURI, error);
}else{
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingString:targetFolder];
NSError *error = nil;
if(![[NSFileManager defaultManager] fileExistsAtPath:dataPath]){
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:&error];
}
NSString *filePath = [NSString stringWithFormat:#"%#/%#%#", documentsDirectory,targetFolder,targetFilename];
return [urlData writeToFile:filePath atomically:YES];
}
}
}
#catch(NSException * e){
NSLog(#"Error download: %#",e);
}
return false;
}
what am i doing wrong?
You try to set NSURLIsExcludedFromBackupKey for the http://-Url you download from the web. That won't work.
You have to set this key-value pair for the actual file that is saved on the device.
Additionally you are not supposed to set this value to the string #"YES", you must use a NSNumber object representing the boolean value YES.
For example:
NSString *filePath = [NSString stringWithFormat:#"%#/%#%#", documentsDirectory,targetFolder,targetFilename];
if ([urlData writeToFile:filePath atomically:YES]) {
// did write correctly
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
if(![fileURL setResourceValue:#YES forKey:NSURLIsExcludedFromBackupKey error:&error]){
NSLog(#"KCDM: Error excluding %# from backup %#", fileURI, error);
return NO;
}
// could set NSURLIsExcludedFromBackupKey
return YES;
}
// could not write to file
return NO;
IfIDieAppDelegate.m
- (void)createEditableCopyOfDatabaseIfNeeded
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"DeathDiary.sqlite"];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"IfIDie.sqlite"];
NSLog(#"%#",writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (success==YES)
{
NSLog(#"Database Already Exists");
return;
}
else {
NSLog(#"New Database Created");
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"IfIDie.sqlite"];
NSLog(#"Default : %#",defaultDBPath);
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"IfIDie.sqlite"]];
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;
}
NoteEditController.m
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
if(saveChanges){
// Save any changes to note
if([[noteTextView text] length] > 0){
if(noteToEdit){
// Edit Note
if([[titleField text] length] <= 0)
[noteToEdit setNoteTitle:[noteTextView text]];
else
[noteToEdit setNoteTitle:[titleField text]];
[noteToEdit setNoteText:[noteTextView text]];
} else {
// New Note
Note *newNote = [NSEntityDescription
insertNewObjectForEntityForName:#"Note" inManagedObjectContext:context];
if([[titleField text] length] <= 0)
[newNote setNoteTitle:[noteTextView text]];
else
[newNote setNoteTitle:[titleField text]];
[newNote setNoteText:[noteTextView text]];
NSLog(#"data saved");
}
} else {
// Remove note (zero length)
if(noteToEdit){
[context deleteObject:noteToEdit];
}
}
}
}
Everything seems going ok here but still data is not saving to table.what could be wrong?
is there something with database reload?if, then i m not getting how to resolve.
no error is there.it shows nslog of data saved but not saved to table.
You will need to save the context to actually commit your changes at the end of viewWillDisappear: method
NSError *error = nil;
if (![context save: &error]) {
// Couldn't save
}
All,
I have the following code in my project:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSError *error = nil;
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"My_Model.sqlite"];
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
__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;
}
- (NSString *)applicationDocumentsDirectory
{
NSError *err = nil;
return [NSString stringWithContentsOfURL:[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] encoding:NSUTF8StringEncoding error:&err];
}
And I am receiving an error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSURL initFileURLWithPath:]: nil string parameter'
What I don't understand is why this is returning s nil? I have referenced a book for this code and it seems to think it should work all ok. Any ideas?
Thanks in advance
Paul
Your -applicationDocumentsDirectory method looks very strange to me. Instead of returning the path of the documents dir, you try to actually return the contents of this directory (which is probably not possible to read, hence it will return nil).
Try replacing the method with
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
}
I added a new model version, and I set the core data model to use that new version, but I get this error when the application tries to start.
"The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store."
I'm guessing the problem is that the current persistent store is the old version of the model. Is there a way to just delete it so it makes a new one? I don't care about saving any of that data.
You have to migrate between versions. According to Apple's docs, if the changes are simple, you can do lightweight migration.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweight.html#//apple_ref/doc/uid/TP40008426-SW1
Adding these options to the NSPersistentStoreCoordinator seemed to work.
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:#"YOURAPP.storedata"];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:options error:&error]) {
[[NSApplication sharedApplication] presentError:error];
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
return nil;
}
return persistentStoreCoordinator;
In answer to your question, "Is there a way to delete it so it just makes a new one ?"
Yes.
Just change the persistentStoreCoordinator getter in your App Delegate as follows:
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (persistentStoreCoordinator) return persistentStoreCoordinator;
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSAssert(NO, #"Managed object model is nil");
NSLog(#"%#:%s No model to generate a store from", [self class], (char *)_cmd);
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *applicationSupportDirectory = [self applicationSupportDirectory];
NSError *error = nil;
if ( ![fileManager fileExistsAtPath:applicationSupportDirectory isDirectory:NULL] ) {
if (![fileManager createDirectoryAtPath:applicationSupportDirectory withIntermediateDirectories:NO attributes:nil error:&error]) {
NSAssert(NO, ([NSString stringWithFormat:#"Failed to create App Support directory %# : %#", applicationSupportDirectory,error]));
NSLog(#"Error creating application support directory at %# : %#",applicationSupportDirectory,error);
return nil;
}
}
NSURL *url = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: #"storedata"]];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: mom];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType
configuration:nil
URL:url
options:nil
error:&error]){
// EDIT: if error opening persistent store, remove it and create a new one
if([[error domain] isEqualToString:#"NSCocoaErrorDomain"] && [error code] == 134100) {
NSLog(#"Core Data model was updated. Deleting old persistent store.");
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType
configuration:nil
URL:url
options:nil
error:&error]){
[[NSApplication sharedApplication] presentError:error];
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
return nil;
}
} else {
[[NSApplication sharedApplication] presentError:error];
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
return nil;
}
//
}
return persistentStoreCoordinator;
}
Figure out where your app stored the document and put it in the trash.
But as a extended comment you may wish to examine the possibilities around both explicit and implicit migration in NSPersistentStoreCoordinator and the options in.
- (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURL options:(NSDictionary *)options error:(NSError **)error
Depending how different the versions are you can get it to happen automagically by passing NSMigratePersistentStoresAutomaticallyOption & NSInferMappingModelAutomaticallyOption
theres also
- (NSPersistentStore *)migratePersistentStore:(NSPersistentStore *)store toURL:(NSURL *)URL options:(NSDictionary *)options withType:(NSString *)storeType error:(NSError **)error
how can I remove all objects? I know I can remove one by
[managedObjectContext deleteObject:objToDelete];
is it possible to delete all without iterating all array?
thanks
This function removes the current SQLite db file from disk and creates a new one. It's much faster than any iterative delete.
-(void)deleteAndRecreateStore{
NSPersistentStore * store = [[self.persistentStoreCoordinator persistentStores] lastObject];
NSError * error;
[self.persistentStoreCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtURL:[store URL] error:&error];
__managedObjectContext = nil;
__persistentStoreCoordinator = nil;
[self managedObjectContext];//Rebuild The CoreData Stack
}
If you want to call this outside Application Delegate (assuming boilerplate CoreData integration) you can use this to get a reference to your app delegate:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
Don't forget to import the header.
Marking objects for deletion and then saving works the way it does because Core Data still needs to run the validation rules for all of the objects being deleted. After all, an object can refuse deletion based on how it responds to -validateForDelete:.
If:
you truly want to delete everything in a persistent store
and you don't care about whether the objects in that persistent store say they're valid for deletion
Then:
tear down the Core Data stack that's using that persistent store
and delete the persistent store's file.
this is what I do to "reset" my data store:
- (BOOL)resetDatastore
{
[[self managedObjectContext] lock];
[[self managedObjectContext] reset];
NSPersistentStore *store = [[[self persistentStoreCoordinator] persistentStores] lastObject];
BOOL resetOk = NO;
if (store)
{
NSURL *storeUrl = store.URL;
NSError *error;
if ([[self persistentStoreCoordinator] removePersistentStore:store error:&error])
{
[[self persistentStoreCoordinator] release];
__persistentStoreCoordinator = nil;
[[self managedObjectContext] release];
__managedObjectContext = nil;
if (![[NSFileManager defaultManager] removeItemAtPath:storeUrl.path error:&error])
{
NSLog(#"\nresetDatastore. Error removing file of persistent store: %#",
[error localizedDescription]);
resetOk = NO;
}
else
{
//now recreate persistent store
[self persistentStoreCoordinator];
[[self managedObjectContext] unlock];
resetOk = YES;
}
}
else
{
NSLog(#"\nresetDatastore. Error removing persistent store: %#",
[error localizedDescription]);
resetOk = NO;
}
return resetOk;
}
else
{
NSLog(#"\nresetDatastore. Could not find the persistent store");
return resetOk;
}
}
You can also just tear down the stack (releasing the NSManagedObjectContext, NSPersistentStore and NSManagedObjectModel) and delete the file. Probably would be faster than iterating over your entire database and deleting each object individually.
Also, it is unlikely they will provide this functionality in the future because it is easy to delete the file. However if you feel it is important then file a radar and let Apple know. Otherwise they won't know how many people want this feature.
Just iterate the array and delete them. There isn't a defined method for deleting them all.
When you remove all the cache and documents, you are deleting the database. Not is necesary call to managedObjectContext
NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSArray *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSMutableArray *paths = [NSMutableArray array];
[paths addObjectsFromArray:documents];
[paths addObjectsFromArray:caches];
for (NSUInteger i = 0; i < [paths count]; i++) {
NSString *folderPath = [paths objectAtIndex:i];
NSLog(#"Attempting to remove contents for: %#", folderPath);
//Remove all cached data in the local app directory
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:&error];
for (NSString *strName in dirContents) {
[[NSFileManager defaultManager] removeItemAtPath:[folderPath stringByAppendingPathComponent:strName] error:&error];
if (error != nil) {
NSLog(#"Error removing item: %# : %#", strName, error.description);
} else {
NSLog(#"Removed item: %#", strName);
}
}
}
I used stifin's code and updated it to use -performBlockAndWait:
- (BOOL)reset
{
__block BOOL result = YES;
[[self mainContext] performBlockAndWait:^{
[[self mainContext] reset];
NSArray* stores = [[self persistentStoreCoordinator] persistentStores];
_mainContext = nil;
_persistedContext = nil;
for(NSPersistentStore* store in stores) {
NSError* error;
if(![[self persistentStoreCoordinator] removePersistentStore:store error:&error]) {
debuglog(#"Error removing persistent store: %#", [error localizedDescription]);
result = NO;
}
else {
if(![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
debuglog(#"Error removing file of persistent store: %#", [error localizedDescription]);
result = NO;
}
}
}
_persistentStoreCoordinator = nil;
}];
return result;
}