I'm working on an app that using core data, and everything is working so far. But I haven't used an image in it yet. I would like to do so but I don't know where to start. I would like to have the user click a button that gives them the option of locations of where they can get images. Also, is it possible to have a user just enter the URL of an image and have it save to core data? Thats really it so if someone could point me in the right direction I would appreciate it. Thanks!
My code so far: https://github.com/jackintosh7/Core-Data
EDIT - I updated some of the references in the saveImgData:fromURL to match the parameters passed in the method. I used a variation of this method within an NSURL protocol to intercept requests and cache specific ones. Some of the entity parameters may not apply to your question. Just disregard those (such as encoding/lastModified/mimeType/response).
To save an image to CoreData give this a try.
First, download your image. A user can enter the url in a text entry field. (The example listed by andrewbuilder will work just fine)
NSURL *imgURL = [NSURL URLWithString:myImgURL];
NSData *imgData = [NSData dataWithContentsOfURL:imgURL];
//a better way would be to do this asynchronously. Google "Lazy Image Loading" and
//you should find a suitable example app from Apple
Setup your CoreData entity to store the image data. Here is a sample that stores more than just the image binary data.
Then save your image data directly to CoreData.
I have a CoreData manager class but you may have your CoreData stack in AppDelegate which is how Apple sets them up.
-(void)saveImgData:(NSData*)myImgData fromURL:(NSString*)urlStr{
//If you have a CoreData manager class do something like this
CoreDataManager *cdm = [CoreDataManager defaultManager];
//Use private queue concurrency type for background saving
NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
//Set up the parent context, in this case is the mainMOC
//If you are using basic CoreData it would be the managedObjectContext on your AppDelegate
ctx.parentContext = cdm.mainMOC;
[ctx performBlock:^{
//performBlock executes on a background thread
CachedURLResponse *cacheResponse = [NSEntityDescription insertNewObjectForEntityForName:#"CachedURLResponse" inManagedObjectContext:ctx];
cacheResponse.relativeURLHash = [NSNumber numberWithInteger:[urlStr hash]];
cacheResponse.data = [myImgData copy];
cacheResponse.response = [NSKeyedArchiver archivedDataWithRootObject:self.response];
cacheResponse.url = [urlStr copy];
cacheResponse.timestamp = [NSDate date];
cacheResponse.mimeType = [self.response.MIMEType copy];
cacheResponse.encoding = [self.response.textEncodingName copy];
if ([self.headers objectForKey:#"Last-Modified"])
cacheResponse.lastModified = [self.headers objectForKey:#"Last-Modified"];
NSError *__block error;
if (![ctx save:&error])
NSLog(#"Error, Cache not saved: %#", error.userInfo);
[cdm.mainMOC performBlockAndWait: ^{
[cdm saveContext];
}];
}];
}
My CoreDataManager.h looks like this:
and my CoreDataManager.m file looks like this:
#import "CoreDataManager.h"
#import <CoreData/CoreData.h>
#implementation CoreDataManager
#synthesize mainMOC = _mainMOC,
managedObjectModel = _mom,
persistentStoreCoordinator = _psc
;
+(CoreDataManager*)defaultManager
{
static CoreDataManager *_defaultMgr = nil;
static dispatch_once_t oncePred;
dispatch_once(&oncePred, ^{
_defaultMgr = [[CoreDataManager alloc] init];
});
return _defaultMgr;
}
-(id)init
{
if (self = [super init])
{
NSError *error = nil;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"MyDatabase.sqlite"];
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"MyDataModelName" withExtension:#"momd"];
_mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
_psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
_mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainMOC setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}
return self;
}
- (void)saveContext
{
NSError *error = nil;
if (self.mainMOC != nil && ([self.mainMOC hasChanges] && ![self.mainMOC save:&error])) {
NSLog(#"CoreData save error: %#, %#", error, [error userInfo]);
}
}
#pragma mark - Core Data Stack
- (NSManagedObjectContext *)mainMOC
{
return _mainMOC;
}
- (NSManagedObjectModel *)managedObjectModel
{
return _mom;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
return _psc;
}
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
Hope that gives you a good starting point. Google "CoreData Class Reference" for some really good reading on the topic.
I learnt a lot from Paul Hegarty's excellent iTunes U / Stanford Uni lectures... his latest is called "Developing iOS Apps for iPhone and iPad" A couple of his lectures specifically address the use of images in Core Data. For what you are wanting to do I recommend you watch lectures 10-13, but really it is worth watching the entire series.
Beyond that, spend some time to read through the Apple Documentation on Core Data, starting here. Samir writes a good response to this stack overflow question.
You have a few options in how you implement the code. While Core Data is capable of managing the saving of images into the structure of your persistent data store (e.g. sqlite), it is important to note that large image files will slow down the fetch process.
Core Data can manage how to store the image for you... by clicking on the Allows External Storage option in the Data Model Inspector... see attached image below.
If however, you are looking to point a user to a URL, it may be worth considering adding (another separate) attribute to your entity Item, for example an attribute named pictureURL.
Use a property in a UIViewController to manage the entity attribute...
#property (nonatomic, retain) NSString * pictureURL;
Then you can add a UITextField to a UIViewController to accept user input for this property pictureURL.
When you have a URL for an image, you can return the image with "getter" code similar to this (where the UIImage is a property of the UIViewController)...
- (UIImage *)image {
NSURL *imageURL = [NSURL URLWithString:self.pictureURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
return [UIImage imageWithData:imageData];
}
Hope that provides you with some direction.
Related
Some background for this issue, I'm trying to include what I think may be relevant to help understand the context.
I am currently adding an linked library which used Core Data to save some user information and a feature which adds an Entity to the pre-existing Core Data model already in the app. Each managedObjectContext has its own instance when created (verified) as well as its own PSC and MOM and neither interact with the other's entities(thus seem to be independent).
The entirety of the following code, errors, and (I believe issue) is in the Main Target of the app. (Hopefully) not the newly added linked library.
The saveContext method is:
- (void)saveContext {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = nil;
// Register
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myManagedObjectContextDidSaveNotificationHandler:) name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];
if (self.managedObjectContext != nil) {
if ([self.managedObjectContext hasChanges]) {
BOOL success = [self.managedObjectContext save:&error];
if (!success) {
[Error showErrorByAppendingString:NSLocalizedString(#"UnableToSaveChanges", nil) withError:error];
} else {
//
}
}
}
// Unregister
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];
});
}
When called, error = nil, success = NO and by forcing the compiler past the exception I get the following:
CoreData: error: exception during obtainPermenantIDsForObjects: Updating max pk failed: attempt to write a readonly database with userInfo of { NSSQLiteErrorDomain = 1032;
}
I have googled, "NSSQLiteErrorDomain = 1032", "obtainPermenantIDsForObjects", and "CoreData readonly database". It does appear that the key primary key for each object is the same, but I am setting that value, I believe sqlite is. I have not found any solutions to help with this. I do have the argument passed on launch, "Concurrency Debug 1" set to on.
I have not implemented obtainPermenantIDsForObjects and I've searched the whole project and cant find its implementation so I think CoreData is using this.
The saveContext method is called on the main queue because thats how my predecessors rolled out the code and I don't have time at the moment to deal with it.
The method calling saveContext (from a background thread):
- (NSMutableArray *)convertRawStepDataTo:(NSMutableArray*)steps
withDates:(NSMutableArray*)dates
inManagedObjectContext:(NSManagedObjectContext*)theMOC {
NSMutableArray *theStepsArray = [[NSMutableArray alloc] init];
// prepare values for chart
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
StepSelector *theSelector = [[StepSelector alloc] init];
NSString* apiSelectionForStep = [theSelector getCurrentSelectionString];
for (int iter = 0; iter < steps.count; iter++) {
NSNumber *currStepValue = [steps objectAtIndex:iter];
// NSNumber *stepCountforIter = [NSNumber numberWithLong:[[steps objectAtIndex:iter] longValue]];
NSNumber* dateForIter = [NSNumber numberWithLong:[[dates objectAtIndex:iter] longLongValue]];
Step *step = [delegate addStepObjectToPersistentStorewithAPI:apiSelectionForStep
andStep:stepCountforIter
andDate:dateForIter
forMOC:theMOC];
[theStepsArray addObject:step];
if (VERBOSE) {
NSLog(#"This is step number %d, with object ID: %#", count, [theMOC objectWithID:step.objectID]);
count++;
}
}
[delegate saveContext];
return theStepsArray;
}
Thats all I can think that might help. The source for the MOC in the main target is the appDelegate which is where all the core data code was written initially.
EDIT Here is the requested PSC code. The store is located in the documents directory. I discovered that these objects are being saved to the Persistent Store.. but the error is still occurs. Se below for PSC code:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [self getStoreURL];
// Rollback journalling mode...
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption,
NSFileProtectionComplete, NSFileProtectionKey,
#{#"journal_mode": #"TRUNCATE"}, NSSQLitePragmasOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error = nil;
self.persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error];
if (!self.persistentStore) {
NSLog(#"Error: %#",error);
[Error showErrorByAppendingString:NSLocalizedString(#"UnableToFindDatabaseFile", nil) withError:error];
}
return persistentStoreCoordinator;
}
-(NSURL *)getStoreURL {
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kSQLFILENAME];
/*
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:SQLFILEPATHRESOURCE ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
return storeUrl;
}
The NSSQLiteErrorDomain key means that this error came from SQLite, and that Core Data is passing it back to you. SQLite defines error 1032 as follows:
The SQLITE_READONLY_DBMOVED error code is an extended error code for SQLITE_READONLY. The SQLITE_READONLY_DBMOVED error code indicates that a database cannot be modified because the database file has been moved since it was opened, and so any attempt to modify the database might result in database corruption if the processes crashes because the rollback journal would not be correctly named.
...which appears to mean that SQLite is making the persistent store file read only because something has happened to it since it was opened, and SQLite is trying to prevent data corruption.
I don't see anything in the code you've posted that is obviously at fault, at least as far as the error code description goes. So I wonder, are you doing anything anywhere else that would directly affect the persistent store file (i.e. touching the file in any way at all instead of going through Core Data fetch/save calls)?
The mention of the rollback journal in the error code description makes me wonder if setting journal_mode to TRUNCATE is related. If it were me, I'd remove that (I don't know what it's intended to accomplish here) or set it to DELETE. At least for testing purposes, anyway, in the hope of understanding the problem better.
Recently I added some concurrent accesses to my different domain models (which represent a subset of the persisted data). My crash reporting shows that I now stumbled upon some errors on iOS7,iOS8 & iOS9, that all seem to be based on
NSSQLiteErrorDomain=26, NSUnderlyingException=File at path does not
appear to be a SQLite database
at the position I marked in the source code
Unfortunately I can't reproduce it and I can't really imagine that the sqlite file doesn't exist on some devices, since I ship it with my application bundle.
Is there a flaw in my code that prevents me from accessing the sqlite file?
Is there another possibility this error can occur from except a missing file?
Heres' my source code for the initialization of my model singleton and the NSManagedObjectContext
+ (id) sharedModel {
static id sharedModel = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^ {
sharedModel = [[self alloc] initWithStoreURL: kDefaultStoreURL];
});
return sharedModel;
}
- (id) initWithStoreURL:(NSURL *)storeURL {
self = [super init];
if (self) {
// create managed object model
NSURL * modelURL = [[NSBundle mainBundle] URLForResource: #"MyApp" withExtension: #"momd"];
NSManagedObjectModel *objectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL];
// create persistent store coordinator
NSError * error = nil;
NSPersistentStoreCoordinator * storeCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objectModel];
if (![storeCoordinator addPersistentStoreWithType: NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
CLS_LOG(#"Unresolved error %#, %#", error, [error userInfo]);
// this is where the error occurs
abort();
}
// init managedObjectContext
context = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType];
[context setPersistentStoreCoordinator:storeCoordinator];
_mainContext = context;
_someDomainModel1 = [[SomeDomainModel1 alloc] initWithContext:_mainContext];
_someDomainModel2 = [[SomeDomainModel2 alloc] initWithContext:_mainContext];
}
return self;
}
initWithStoreURL may have an issue, but if you've been using this same code all along, maybe I am making an incorrect assumption.
You say that your SQLite store file is provided with the bundle, and if that's the case you need to open it readonly and in rollback mode.
NSDictionary *storeOptions = #{
NSReadOnlyPersistentStoreOption:#YES,
NSSQLitePragmasOption:#{#"journal_mode":#"DELETE"}};
Then, use the options when adding the store.
if (![storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:storeOptions
error:&error]) {
}
Also, since you are seeing this, you should log a bunch more information (like the URL and the details of the error object).
However, you are calling abort(), so you should be able to get a stack trace and state information from the crash report as well.
The other thing... look at how you are generating your database. You should make sure that you generate it in rollback mode (journal_mode=DELETE) as well.
I am using CoreData in an iOS application to manage words in a "flash cards" application for people learning new languages.
The problem I'm having is that when I set up the data for a new entity and try to save it to the data store, I get a violation of a UNIQUE CONSTRAINT requirement on the sqlite database. The column in question is the Z_PK column, which I understand to be a PRIMARY KEY created by the iOS core data methods when the data store is initially created.
Here's the UNIQUE CONSTRAINT message I'm getting when I attempt the save:
2015-03-14 09:25:14.427 ghoti[25856:1107373] CoreData: error: (1555)
UNIQUE constraint failed: ZGHOTIENTITY.Z_PK (lldb)
Z is the prefix stuck on all of these SQL columns
GHOTIENTITY is my data store
Z_PK is the primary key
Here's a quick screenshot of the data table inside the sqlite "editor" in firefox:
The definition of the business object entity itself does not include the Z_PK column:
#implementation ghotiEntity
#dynamic numberCorrect;
#dynamic numberIncorrect;
#dynamic likelihoodOfPresentingWord;
#dynamic englishText;
#dynamic hebrewText;
#dynamic phoneticText;
#end
I'd be annoyed - but compliant - if it were as simple as just setting the ghotiEntity.Z_PK to the maximum value of Z_PK in the datastore + 1, but that column is not even part of the entity definition in the first place.
EDIT - A helpful user asked for the code, so I'm adding it here rather than trying to stuff it into a comment:
I'm using the mmBusinessObject collection of core data methods that I've seen floating around the web. Kevin McNeish explains it fully here.
The basic structure is this:
mmBusinessObject.m and .h do a nice job of wrapping the managedObject methods into something "easy to read and deal with". For the sake of readability, I've copied the snippet of two elements [entity createEntities] and [entity saveEntities] here and the full .m file at the end of this post. (note that I have not yet built in all of the error-checking, I'm just trying to make the basic function work.
CREATE A NEW ENTITY:
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
SAVE THE CHANGES
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != 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();
}
}
}
So my operating code (inside my ViewController.m file) does this: (note that ghotiEntity is my specific business object.
- (IBAction)saveButtonAction:(UIButton *)sender {
ghotiEntity *newGhotiEntry = (ghotiEntity *)[ghotiData createEntity];
newGhotiEntry.englishText=self.englishWord.text;
newGhotiEntry.hebrewText=self.hebrewWord.text;
newGhotiEntry.phoneticText=self.phoneticWord.text;
newGhotiEntry.numberCorrect=0;
newGhotiEntry.numberIncorrect=0;
newGhotiEntry.likelihoodOfPresentingWord=0;
[ghotiData saveEntities];
[self enableRegularUI];
[self pickNextCard];
}
It is from the NSLog line in the saveEntities method above that I get the diagnostic information that it is the Z_PK column that has the UNIQUE CONSTRAINT problem.
As promised above, here is the full .m file. I need to make sure that it is clear that the credit for this code goes to either Kevin McNeish as noted above, or any of several other collaborators who have posted the same code in other places... just search mmBusinessObject in google. I think Kevin is the originator and his tutorial is really good. The first few lines in the versions I have seen all have Kevin's name in them!
#import "mmBusinessObject.h"
#implementation mmBusinessObject
// Initialization
- (id)init
{
if ((self = [super init])) {
_copyDatabaseIfNotPresent = YES;
}
return self;
}
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
// Delete the specified entity
- (void) deleteEntity:(NSManagedObject *)entity {
[self.managedObjectContext deleteObject:entity];
}
// Gets entities for the specified request
- (NSMutableArray *)getEntities: (NSString *)entityName sortedBy:(NSSortDescriptor *)sortDescriptor matchingPredicate:(NSPredicate *)predicate
{
NSError *error = nil;
// Create the request object
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Set the entity type to be fetched
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Set the predicate if specified
if (predicate) {
[request setPredicate:predicate];
}
// Set the sort descriptor if specified
if (sortDescriptor) {
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Execute the fetch
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
return mutableFetchResults;
}
// Gets all entities of the default type
- (NSMutableArray *)getAllEntities
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:nil];
}
// Gets entities of the default type matching the predicate
- (NSMutableArray *)getEntitiesMatchingPredicate: (NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Gets entities of the default type matching the predicate string
- (NSMutableArray *)getEntitiesMatchingPredicateString: (NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Get entities of the default type sorted by descriptor matching the predicate
- (NSMutableArray *)getEntitiesSortedBy: (NSSortDescriptor *) sortDescriptor
matchingPredicate:(NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:sortDescriptor matchingPredicate:predicate];
}
// Gets entities of the specified type sorted by descriptor, and matching the predicate string
- (NSMutableArray *)getEntities: (NSString *)entityName
sortedBy:(NSSortDescriptor *)sortDescriptor
matchingPredicateString:(NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:entityName sortedBy:sortDescriptor matchingPredicate:predicate];
}
- (void) registerRelatedObject:(mmBusinessObject *)controllerObject
{
controllerObject.managedObjectContext = self.managedObjectContext;
}
// Saves all changes (insert, update, delete) of entities
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != 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();
}
}
}
#pragma mark -
#pragma mark Core Data stack
/**
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] 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:self.dbName withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// If the sqlite database doesn't already exist, create it
// by copying the sqlite database included in this project
if (self.copyDatabaseIfNotPresent) {
// Get the documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsDir = paths[0];
// Append the name of the database to get the full path
NSString *dbcPath = [docsDir stringByAppendingPathComponent:
[self.dbName stringByAppendingString:#".sqlite"]];
// Create database if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbcPath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:self.dbName ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:dbcPath error:NULL];
}
}
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:
[NSString stringWithFormat:#"%#%#", self.dbName, #".sqlite"]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"%#",storeURL);
if ([error code] == 134100) {
[self performAutomaticLightweightMigration];
}
else
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
return _persistentStoreCoordinator;
}
- (void)performAutomaticLightweightMigration {
NSError *error;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#%#", self.dbName, #".sqlite"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error]){
// Handle the error.
}
}
#pragma mark -
#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
I had the same problem with a Z_PK constraint issue. It stemmed from the fact that I had copied the empty sqlite database from my app's documents folder and prepopulated it using Firefox's SQLite manager add-in. The step I forgot was updating the Z_PrimaryKey table...you have to update the Z_MAX value for each table so that Core Data will know the next Z_PK to use when new records are added to the database. Simply set each Z_Max value with the number of records in each table. Save it and drop it back into your app and all will be well.
Look through your code for all places you create an object for the DB. Make sure that you are not creating objects from multiple threads.
i had the same issue but i had this when i want to update an entry , so i fixed with refreshObject then save like this:
[dataManager.managedObjectContext refreshObject:object mergeChanges:YES];
[dataManager saveContextWithBlock:block];
I have a app that is using core data. The Core Data stack (the context, object graph, persistent store coordinator, and the persistent store) is being created, and I am able to use it without issue. The problem is that the saved data is not persisting, can someone help me with what I am doing wrong? Here is where I create the Core Data stack.
- (void)initializeCoreDataStack
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Favorites"
withExtension:#"momd"];
if (!modelURL)
NSLog(#"MODEL URL NOT INITIALIZED");
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
if (!mom)
NSLog(#"OBJECT MODEL NOT CREATED");
NSPersistentStoreCoordinator * psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (!psc)
NSLog(#"PERSISTENT STORE COORDINATOR NOT CREATED");
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc setPersistentStoreCoordinator:psc];
self.managedObjectContext = moc;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSArray *directory = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask];
NSURL *storeUrl = [directory lastObject];
storeUrl = [storeUrl URLByAppendingPathComponent:#"Favorites.sqlite"];
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeUrl
options:nil
error:&error];
if (!store)
{
NSLog(#"ERROR CREATING STORE: %# %#", error.localizedDescription, error.domain);
// present error to user
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
// do something once the stack is finished being created
NSLog(#"persistent store created");
});
}
});
}
You have to save core data explicitly, otherwise it won't persist. Not hard to solve, though.
In your controller implementation file (e.g. coreDataViewController.m), call this function when you want to save changes to core data
// add this call, whenever you want to save data
// e.g. responding to a UIButton event
[self saveCoreDataContext];
- (void)saveCoreDataContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
My personal experience is that, even if you called [moc save:error], you may not find the content saved while you are running the app from Xcode -> Build and Run on device. However, if you stop the Xcode from running the app, and launch the app from the device by clicking the App Icon, the content is actually persisted.
Just personal experience that I found through trial and error, hope that you see the same thing.
You have to save the MOC.
[mom save:nil];
There's a few things here that stand out to me as odd. First, why are you threading the creation of the NSPersistentStore? Generally, I create the NSPersistentStoreCoordinator, add NSPersistentStores, and then create the contexts. I would suggest doing it in that order unless you have a good reason to do otherwise. While it's not specifically prohibited, you may experience problems adding a NSPersistentStore after the NSManagedObjectContext has been created.
I'm not sure if it is required or not, but I've always explicitly held a strong reference to the NSPersistentStoreCoordinator. It's useful to create multiple contexts, as well. I would suggest doing so in your code.
As everyone else has said, you also need to explicitly save.
I have a pre-existing project to which I've added Core Data models. I added the Core Data framework, added a data model with entities, and included it in my app's target, along with some generated NSManagedObject classes. It compiles nicely, and now I'd like to add some tests for the entities I've created. Following these instructions, I've set up a logic test base class with a setUp method like so:
- (void)setUp {
model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSLog(#"model: %#", model);
coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
store = [coord addPersistentStoreWithType:NSInMemoryStoreType
configuration:nil
URL:nil
options:nil
error:NULL];
ctx = [[NSManagedObjectContext alloc] init];
[ctx setPersistentStoreCoordinator:coord];
}
This compiles and all the objects are created. However, the model has no entities! The NSLog() output looks like so:
2011-10-29 23:56:58.941 otest[42682:3b03] model: (<NSManagedObjectModel: 0x19c6780>) isEditable 1, entities {
}, fetch request templates {
}
So where are my entities? I've poked around the bundle, and there are no .momd files, either. Have I missed some crucial step to get my models to build?
I did some additional Duck Duck Going and managed to find the information I needed in the this answer. The upshot is that, because a test target doesn't use a "main" bundle, I have to instantiate the test bundle. So instead of this line:
model = [NSManagedObjectModel mergedModelFromBundles:nil];
I now have these three lines:
NSBundle *bundle = [NSBundle bundleWithIdentifier:#"com.example.LogicTests"];
NSURL *url = [bundle URLForResource:#"MyModels" withExtension:#"momd"];
model = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
The bundle identifier comes directly from my target build info, while "MyModels" comes from my data model file, which is named "MyModels.xcdatamodeld" and included in the app bundle as "MyModels.momd". And that, of course, contains my models.
Look here. I'm using code that is generated when you are creating project with CoreData. I hope that would help you to solve your problems:
pragma mark Core Data stack
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *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] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
*/
- (NSManagedObjectModel *)managedObjectModel
{
if (managedObjectModel != nil)
{
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (persistentStoreCoordinator != nil)
{
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"CoreDB.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible
* The schema for the persistent store is incompatible with current managed object model
Check the error message to determine what the actual problem was.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
If your database is within / from a framework, you need to load it using its corresponding Bundle.
I found a solution in Swift with MagicalRecord:
let frameworkBundle = Bundle(for: AClassFromTheFramework.self)
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [frameworkBundle])
MagicalRecord.setShouldAutoCreateManagedObjectModel(false)
NSManagedObjectModel.mr_setDefaultManagedObjectModel(managedObjectModel)
MagicalRecord.setupCoreDataStack(withAutoMigratingSqliteStoreNamed: "db.sqlite")
Loading the managaed object model from the bundle and setShouldAutoCreateManagedObjectModel set to false did the trick!