Access to core data database in other class - ios

i have a core data database that start from the AppDelegate, and there i'll do this:
MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil] autorelease];
masterViewController.managedObjectContext = self.managedObjectContext;
so in this way i can access to the database in the master view, then i want access to the core data in other classes, and i do this:
id delegateContext = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [delegateContext managedObjectContext];
but when i add, and access the information, i have some bad Exc access and other error in various part of the code when i try to access to the information of the database, so i think that maybe i have used in bad way the core data, to access the information from other classes.
maybe i have to release the delegatecontext?...i haven't release it in any class i have used it, and if i have release it, where i have do it?
anyone can help me?

Problem is you are taking delegateContext of type id instead of NSManagedObjectContext and assigning it to *contextin these two lines.
id delegateContext = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [delegateContext managedObjectContext];
How about adding these lines whenever you need to save or access managedObjectContext, check it if its nil if YES copy it from Appdelegate.
if (managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
Just try to remove your these two lines and add the above if condition I am suggesting. Should work.
Update 1: As you are asking I am mentioning steps for creating
Appdelegate's instance to access global properties.
#import "AppDelegate.h"
after #implementation create instance of Appdelegate
AppDelegate *app;
in viewDidLoad method...
app=(AppDelegate *)[[UIApplication sharedApplication]delegate];

Related

Illegal access to Core Data context: what thread is the owner of the context provided in AppDelegate?

I have a class with a method like this:
- (void)getEntities
{
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = appDelegate.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MyEntity"];
NSArray *entities = [mainContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *item in entities) {
NSLog(#"Name: %#", ((MyEntity *)item).name);
}
}
I have enabled the com.apple.CoreData.ConcurrencyDebug 1 and when I call this method (in main thread) I get the error:
CoreData: error: The current thread is not the recognized owner of this NSManagedObjectContext(0x16d40160). Illegal access during executeFetchRequest:error:
However, if I do:
- (void)getEntities
{
NSManagedObjectContext *privateContext = [CoreDataStack getPrivateContext];
if (privateContext != nil) {
[privateContext performBlock: ^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MyEntity"];
NSArray *entities = [privateContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *item in entities) {
NSLog(#"Name: %#", ((MyEntity *)item).name);
}
}];
}
}
And I call it also in main thread, it does not seem to cause such Core Data error... why? I've been always assuming that the NSManagedObjectContext provided by default in AppDelegate and I'm retrieving belongs to main thread... What I'm doing wrong or missing?
The entities I'm trying to fetch using the context from AppDelegate where previously saved from a context that was created in a private queue (similar to the one I create in my second code snippet). Could that be the cause of the error? Such private context was not a child of any other context, and I had no error when saving it.
I need to get the entities I have already stored by using a private context in the main thread, to keep them in an NSArray in the implementation of an UIViewController.
Thanks so much in advance.
It seems that the problem wasn't in the method but my initialization of the context in AppDelegate: before calling the method in the question, and in certain scenario I was retrieving the context the first time from a queue that wasn't the main thread, and that seemed to be causing the context to be initialized in that other queue.

difference in the way nsmangedobjectcontext is retrieved

What is the difference between the global nsmanagedobjectcontext retrieved from appdelegate and the nsmanagedobjectcontext retrieved from nsmanagedobject
1)
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = delegate.managedObjectContext;
2)
NSManagedObject *employee1 = [[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:context];
NSManagedObjectContext *context = employee1.managedObjectContext;
If you get an instance of NSManagedObjectContext from your app delegate, it's whatever instance you create in your app delegate, configured in whatever way you have configured it.
If you ask a managed object for the value of its managedObjectContext property, you get whatever context was used to create it or fetch it.
These might be the same or they might not be, depending on how many instances of NSManagedObject you have created and how you are using them.

CoreData: ObjectID Error: "No known class method for selector 'managedObjectContext'

#implementation FirstScene
...
- (void)nextScene {
Meetings *meetings = (Meetings *) [NSEntityDescription insertNewObjectForEntityForName:#"Meetings" inManagedObjectContext:self.managedObjectContext];
NSManagedObjectID* objectID = [meetings objectID];
[secondScene setObjectID:objectID];
}
...
#end
#implementation SecondScene
....
- (void)viewDidLoad
{
[super viewDidLoad];
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
+ (void)setObjectID:(NSManagedObjectID*)objectID {
NSManagedObjectContext *context = [self managedObjectContext]; // ERROR
Meetings *theSameMeetings = (Meetings *)[context objectWithID:objectID];
}
...
#end
I'm getting an error "No known class method for selector 'managedObjectContext' in the following code:
NSManagedObjectContext *context = [self managedObjectContext];
My objective here is to pass the objectID of *meetings (managedObjectContext) in FirstScene to the SecondScene so that I can continue to add to the entity *meetings attributes. Thanks.
That's not a Core Data error, it's a basic Objective-C error. You're in this method:
+ (void)setObjectID:(NSManagedObjectID*)objectID;
The "+" says that this is a class method, not an instance method. So self in this case is the SecondScene class, not any particular instance of that class. When you try to do this:
NSManagedObjectContext *context = [self managedObjectContext];
...you're trying to call a method named +managedObjectContext, i.e. a class method with that name. That apparently doesn't exist, which isn't too surprising.
I suspect you intended for setObjectID: to be an instance method, which means it should have a - instead of a + at the start of the line.
Brother, as it says you do not have 'managedObjectContext' method in your 'SecondScene'. is it?
Apparently you are a beginner to core data and you have just stepped into it.
however, Go to AppDelegate of your project, and search for '-(void)managedObjectContext' and you will see that there is a manageobjectcontext method there, replace [self manageobjectcontext] with the method from AppDelegate.
This will allow you to have access to appdelegate
YourAppDelegateClass *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate]; and now call [appDelegate managedObjectContext];
Hope this helps.
By calling [self managedObjectContext] the compiler is looking for a method name in the current target that is -(void)managedObjectContext which clearly doesn't exist. The best way to pass an object ID is set a property and synthesize that property, that will allow you to assign the objectID to an instance of meeting, and then retrieve it from said instance.
--EDIT--
In whichever class you need to access your ManagedObjectID you want to declare your delegate as such:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
Then in your setObjectID method you can call:
NSManagedObjectContext *context = [appDelegate managedObjectContext];

Core data Background slow and background save, sqlite background and mergeChangesFromContextDidSaveNotification

I'm writing an update for my app, i need to insert inside the db 30 images. Now i'm using core data, and it saves correctly but it takes 10 seconds... So i have decided to send in background the saving process. I have a for cycle that works the image array, each turn i create an NSmanagedObject and i save it. With this base i have try different solution, nothing works correctly:
1-----------------------
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Add code here to do background processing
//context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSManagedObjectContext *contextTemp = [[NSManagedObjectContext alloc] init];
NSPersistentStoreCoordinator *store=[(RecipesAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
contextTemp.persistentStoreCoordinator=store;
Recipe *recipe = (Recipe *)[contextTemp objectWithID:MoID];
NSManagedObject *image = [NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:contextTemp];
[image setValue:[[ArrayDizionariImmagini objectAtIndex:i] valueForKey:#"image"] forKey:#"image"];
[image setValue:[NSDate date] forKey:#"data"];
recipe.image=image;
[contextTemp save:nil];
});
This method saves all in the correct way but it takes 10 seconds,just like my first attemp in mainthread,so it's useless...
2-----------------------------------------------------
dispatch_queue_t request_queue = dispatch_queue_create("com.doeatraw.saveimages", NULL);
dispatch_async(request_queue, ^{
// Create a new managed object context
// Set its persistent store coordinator
AppDelegate *theDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init];
[newMoc setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]];
// Register for context save changes notification
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:newMoc];
BOOL success = [newMoc save:nil];
});
dispatch_release(request_queue);
- (void)mergeChanges:(NSNotification*)notification
{
context = [(RecipesAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[context performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
In this attemp i save a temporary ManagedObjectContext only at the end of all the update and then i try to merge changes. In this way i can save all the images in background an i my UI remain responsive all the time. But if i close and i reopen the app all my savings are losts...so it became clear that core data didn't any real merge.
3------------------------------------------------------------
I have also try to direct insert all the data by sql. I have put all in background and it seems ok, ui is responsive and all data are inserted...Anyway something go wrong. When i try to access the same table (by launching other indipendent methods in the app that interrogates the same table) sql crash the app without error in log(only one time i have received the error "Constraint Failed"). I'm able to extract and visualize the inserted images but i cant mad another insert (by core data) in the table.
Someone can help me? I know that NSManagedObjectContext isn't thread safe, but i have tried to follow the documentation guidelines in some of my attemps...Maybe i miss something...
What i'm trying to save is an nsmanagedobject with the image as relationship. How i can real merge my contexts? Or what i can do with the error sqlite Constraint Failed?
Most probably your overhead comes from creating a new context each time you save an image. That's an expensive operation. You only need 2 contexts, one for the main queue and the second for a serial background queue. Also, please note that Apple advises against storing images in the database, the experience I have says the same thing. You should only keep the image path in the database, and keep the image on the file system.

Store userid in memory IOS

I made an IOS Application with a login function. I use the ASIHTTPRequest for this to check if the user exists in a MySQL database. All works fine but i want to pass the userid trough other viewcontrollers or retrieve it on other parts of the app.
Can someone push me in the right direction how to do this in the best and most secure way?
This is my the way i do a POST HTTP request to a PHP script:
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:#"*SOME_URL*"]];
In a callback i can retrieve the users ID, but i want to store this ID in the memory of the app for further use.
Thanks.
You can store userId in the AppDelegate.
Declare a userId property in AppDelegate.h with the following code:
#property (nonatomic, strong) NSString *userId
And then synthesize it in AppDelegate.m with the following code:
#synthesize userId;
You can then use userId from anywhere in your app. Add an import for the AppDelegate in the class you want to use userId, as follows:
#import "AppDelegate.h"
To retrieve the userId use the following code:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *userId = [appDelegate userId];
And to set the userId use the following code:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate setUserId:YouValueRetrievedByInternetOrWhateverYouWant];
Have a session class that works as a singleton and can be accessed with a static method.
To do this you can add a static method
+ (Session *)sharedInstace {
static Session *session;
if (!session)
session = [Session new]; // there are more proper ways to instantiate a singleton class, not gonna get into it now
return session.
}
On that class .h you should add a NSString (or a class of your choice or whatever you want) and you can access it from wherever with [Session sharedInstance].userID

Resources