Adding CoreData into an existing project using Objective-C - ios

I'm trying to create a data storage for my application using CoreData. From what I know, Xcode 8 CoreData is using persistentContainer instead of managedObjectContext.
I've created a data model with my required entities and created an NSManagedObject subclass from the Editor menu.
My problem is that when I want to use the persistentContainer, there is no identifier found.
#import "UserCredentials+CoreDataClass.h"
//Fetch all username to array
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]initWithEntityName:#"UserCredentials"];
NSError *requestError = nil;
//I couldn't find the persistent container even though I had imported my header file.
NSArray *usernames = [self.persistentContainer.viewContext executeFetchRequest:fetchRequest error:&requestError];
I realised that my CoreDataClass did not even have the property persistentContainer at all. Where can I declare this at, so I can access my data storage?

I am assuming you have selected core data option while creating your object. Your object context is null because it is store into AppDelegate. So you need to get context reference from appdelegate like below.
NSManagedObjectContext *context = ((AppDelegate*)[[UIApplication sharedApplication] delegate]).persistentContainer.viewContext;
NSArray *usernames = [context executeFetchRequest:fetchRequest error:&requestError];

You should create property
//.h file
#property (readonly, strong) NSPersistentContainer *persistentContainer;
//.m file
#synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
#synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:#"CoreDataModel"]; //e.g. CoreDataModel.xcdatamodeld
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
// 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
RLog(#"Unresolved error %#, %#", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}

Related

iOS CoreData can't get the property using objective-c

I try to using objective-c write the core data.
My swift code can get the User Entites like below
swift3:
let appDel = UIApplication.shared.delegate as? AppDelegate;
guard let context = appDel?.persistentContainer.viewContext else{ return }
let User = User(context: context);
I using objective-c write in the viewdidload.
But I can't persistentContainer property.
like below:
Objective-c
UIApplication *application = [UIApplication sharedApplication];
NSManagedObjectContext *context = (AppDelegate*)(application.delegate).
persistentContainer.viewContext;
Have anyone can resolve the problem about can't get persistentContainer property?
I using the Xcode8.2.1 objective-c project.
Thank you very much.
*First we should import core data framework in Appdelegate and set the property of persistentContainer
In Appdelegate.h file
#property (readonly, strong) NSPersistentContainer *persistentContainer;
- (void)saveContext;
In Appdelegate.m file
#synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
#synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:#"NSURL"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
// 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
NSLog(#"Unresolved error %#, %#", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context 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();
}
}

steps for use core data as database in my iOS app in Objective C

anyone can describe me about core data?
I want to create worksheet which store day activity record and that data stored in local file.
I think core data is best to store locally.
Thanks in advance.
You should see CoreData not as a database, but as a way to manage a graph of object.
You can then store this graph in different places (transparently from the application point of view) such as memory, XML, sqlite, and I think custom binary file.
What you usually do is to write the model in a core data model.
Each object is either an instance of NSManagedObject (which you can query / work with with methods such as valueForKey:, setValueForKey: etc) or subclasses of that class. This subclasses can be autogenerated directly from Xcode, and at this point you almost forget you are working with CoreData. Every attribute is a #property, every to-many relationship is a NSSet.
You get back to the fact that you are using CoreData when you create and want to save the object. In this case you have to get the 'context' in which the object resides, and call method on it (e.g. save)
There is full of tutorial and documentation on the web about CoreData.
In my opinion the core point is.. don't think at it as a relational database. "Be more object oriented" :)
To getting started you can take a look at:
http://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started
The for more complex stuff the apple doc is ok
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html#//apple_ref/doc/uid/TP30001200-SW1
Yes you are right, Core data is bast way to store data in iOS applications.
With new XCode all you need to do is when creating new project click check box that you will use coreData.
This will create yourPorject.xcdatamodeld file and some methods inside your AppDelegate file :
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "result.PingPong" 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:#"PingPong" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"PingPong.sqlite"];
NSError *error = nil;
NSString *failureReason = #"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = #"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this 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 _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];
return _managedObjectContext;
}
#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();
}
}
}
(I case it didn't)
After that you create your entity object inside yourPorject.xcdatamodeld, by clicking on AddEntity, name it and inside with Add Attribute add all your attributes.
After that click on menu : Editor -> Create NSManagedObject subclasses. This will automatically create object for you.
All you need to do to save object into database is
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
YourObject * o = [NSEntityDescription insertNewObjectForEntityForName:#"YourObject" inManagedObjectContext:context];
o.attribute1 = attribute1;
o.attribute2 = attribute2;
[context save:nil];
To fetch all object you will need this :
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSError *fetchError;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourObject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&fetchError];
if (fetchError != nil) {
NSLog(#"Error fetching database request. Reason: %#", fetchError);
}
I hope it will help you for start.
Marko

Objects Not Being Saved to Core Data Database

I am trying to add objects to a Core Data Database using the following code:
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] init];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistentStoreCoordinator];
Feed *feed = [NSEntityDescription insertNewObjectForEntityForName:#"Feed" inManagedObjectContext:managedObjectContext];
feed.feedID = dictionary[#"feed_id"];
feed.siteURL = dictionary[#"site_url"];
feed.title = dictionary[#"title"];
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Error, couldn't save: %#", [error localizedDescription]);
}
Feed is a subclass of NSManagedObject. The persistentStoreCoordinator returned from the sharedCoreDataController (a singleton) is the persistentStoreCoordinator from a UIManagedDocument (created or opened when the app launches). As far as I can tell, the document is being created or opened successfully. I am running this code in the simulator, and I'm looking in the directory in which I am saving the Database (the apps Documents directory), but the persistentStore file is not being updated to reflect the new objects being added. Am I doing something wrong? I should also point out that the above code is being executed multiple times on a concurrent, asynchronous queue.
Any help would be much appreciated, thanks in advance.
Update: After the suggestions from Alexander and Duncan, the code above has been updated to reflect the changes. Sadly, however, I haven't noticed any difference (the new data is not appearing in the persistentStore file).
Have you called the line to save your managedObjectContext? How about this:
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Error, couldn't save: %#", [error localizedDescription]);
}
Try using something like this
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] init];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistentStoreCoordinator];
for (NSDictionary *dictionary in arrayOfData) {
Feed *feed = [NSEntityDescription insertNewObjectForEntityForName:#"Feed" inManagedObjectContext:managedObjectContext];
feed.feedID = dictionary[#"feed_id"];
feed.siteURL = dictionary[#"site_url"];
feed.title = dictionary[#"title"];
}
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Error, couldn't save: %#", [error localizedDescription]);
}
I would avoid creating a managedObjectContext for each object you insert.
First, using UIManagedDocument is not recommended. It is not intended to be your single app wide context. It is meant for document style applications.
Second, the NSManagedObjectContext that is exposed from the UIManagedDocument doesn't have a NSPersistentStoreCoordinator attached to it. It is a child context of a private NSManagedObjectContext that then has a NSPersistentStoreCoordinator. I suspect that if you used the debugger you may find that your NSManagedObjectContext is missing its NSPersistentStoreCoordinator.
In any event, you are using UIManagedDocument and then trying to attach another NSManagedObjectContext to the same NSPersistentStoreCoordinator. That is a bad design and you should at a minimum remove the UIManagedDocument or stop creating a new NSManagedObjectContext.
What is the point of the new context? What are you trying to solve with this code?

NSmanaged context threads

I use a singleton for working with arrays etc. cross the views in the application.
To initialize the singleton and the NSManagedObjectContext, so that I can fetch objects, I use:
+(DataControllerSingleton *)singleDataController
{
static DataControllerSingleton * single=nil;
#synchronized(self)
{
if(!single)
{
single = [[DataControllerSingleton alloc] init];
NSManagedObjectContext *context = [single.fetchedResultsController managedObjectContext];
single.masterCareList = [[NSMutableArray alloc] init];
}
}
return single;
}
When I insert a new object that object will not show up in display functions until I restart the application. I insert new object through this method in the singleton class:
- (void)insertNewObject:(Care *)care
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:
[entity name] inManagedObjectContext:self.managedObjectContext];
NSString *fileName = care.pictureURL;
NSString *text = care.causeText;
NSDate *date = care.date;
NSData *imgData = care.imageData;
[newManagedObject setValue:fileName forKey:#"urlPath"];
[newManagedObject setValue:text forKey:#"name"];
[newManagedObject setValue:date forKey:#"date"];
[newManagedObject setValue:imgData forKey:#"imageData"];
// Save the context.
[self saveContext];
NSError *error = nil;
if (![context 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();
}
}
My count method is the way I can tell that the new object is not included until I restart the application. The count method is also in the singleton as well.
- (NSUInteger)countOfList
{
NSArray *fetchedData = [_fetchedResultsController fetchedObjects];
return [fetchedData count];
}
When calling singleton I use:
DataControllerSingleton *singletonData = [DataControllerSingleton singleDataController];
[singletonData insertNewObject:care];
managedObjectContext property:
.h:
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
.m:
#implementation DataControllerSingleton
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
Why will not my new object show up in ex count until I restart application?
Am I somehow using different threads with different contexts, or different fethedResultsController or different singleton (shouldnt be possible right?)??
I added these two lines, which are not included in the genereated CoreData Stack, and it now works fine.
In singleton header:
#interface DataControllerSingleton : NSObject <NSFetchedResultsControllerDelegate>
In implementation file,
(NSFetchedResultsController *)fetchedResultsController {
_fetchedResultsController.delegate = self;
As I understand from your question, you are using a table or similar.
If you want to update the table as soon as you save the context you need to:
Reload the data table [table reloadData];
or implement in the correct delegate methods of (take a look to How To Use NSFetchedResultsController)
If you follow the first option, you can just do a save in the context and call realod data on the table.
NSError *error = nil;
if (![context 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();
}
[table reloadData];
NOTE THAT YOU ARE CALLING THE SAVE TWICE Do it once. In this case I suppose that [self saveContext]; does the saving as above.
If you follow the second approach, the data reload woul be handled for you.
Hope that helps.
Edit
The delegate of your fetched results controller should be a view controller (the one that contains the table). Do not put it in your singleton!!

Execution_BAD-ACCESS when deleting data

I am showing data in table view using NSFetchedResultsController. Now when data reaches from server I need to delete all data present in the sqlite database.
Now when I delete data from database using given below code it sometime crashes (not always) giving this error:
Execution_BAD-ACCESS (code=2, address=0x0)
on this line
if (![moc save:&saveError]) {
.h
#property (readonly, retain, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, retain, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, retain, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
.m
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
NSManagedObjectContext *moc = [delegate managedObjectContext];
NSFetchRequest * allCategories = [[NSFetchRequest alloc] init];
[allCategories setEntity:[NSEntityDescription entityForName:#"Categories" inManagedObjectContext:moc]];
[allCategories setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * dataArray = [moc executeFetchRequest:allCategories error:&error];
//error handling goes here
[NSFetchedResultsController deleteCacheWithName:#"RootDetail"];
for (Categories *cat in dataArray) {
[moc deleteObject:cat];
}
NSError *saveError = nil;
if (![moc save:&saveError]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
[allCategories release];
I check throughly now i found that this problem is coming when i vist the DetailPageController and go back(using UINavigationController popNavigationController:) and then if i wist DetailPageController then it crashes.
giving following errror
-[DetailPageController controllerWillChangeContent:]: message sent to deallocated instance 0x11f52a90*
The problem is of NSManageObjectContext. So the fix is always use new created object of NSManageObjectContext otherwise it will create problems.
Based on your comment
I am using operation queue. so i enter data on main thread. 2. you are
saying that each thread should have separated instance of context. But
i think there should be only one main instance of context.
No. You MUST follow the documentation about Concurrency with Core Data
Create a separate managed object context for each thread and share a
single persistent store coordinator. This is the typically-recommended
approach.
or
Create a separate managed object context and persistent store
coordinator for each thread. This approach provides for greater
concurrency at the expense of greater complexity (particularly if you
need to communicate changes between different contexts) and increased
memory usage.
or
use new Core Data APIs.
Original question
If you provide some other details about the crash, I think we can help you. What about delegate?
In the meantime, some hints for you.
1) Enable zombies in Xcode
How to enable NSZombie in Xcode?
2) Use the right context
Why do you use the following?
NSManagedObjectContext *moc = [delegate managedObjectContext];
just use
NSManagedObjectContext *moc = [self managedObjectContext];
This could be the origin of the problem. But without details I'm not very sure.
So, when you create this controller from external, set the managed object context property correctly.
yourController.managedObjectContext = theContextYouWantToShare;
3) Error handling
NSError * error = nil;
NSArray * dataArray = [moc executeFetchRequest:allCategories error:&error];
if(dataArray) {
// manage objects here...
} else {
// handle error here...
}
The answer by flexaddicted is very good (unfortunately I can't comment yet) but remember to be very careful if you have a multi-threaded application (you mention server calls in your question). Make sure that each thread uses its own context, otherwise you will run into problems. This is well docmented in Apple's Core Data documentation.
Or, at the very least, make sure that any call to do something with core data is on the main thread (although this is not ideal as this can block when performing long operations).

Resources