Im a new programmer trying to make a random function using Core Data. What I want is for the label to return a random managedObject from my entity. Here is my code
- (IBAction)randomize{
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Restraunt" inManagedObjectContext:context ];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSError *error;
NSArray *matchingData = [context executeFetchRequest:request error:&error];
NSString *textString;
int RandomNumber;
RandomNumber = arc4random() %[matchingData count];
textString = [matchingData objectAtIndex:RandomNumber];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"restraunt_name like %#",textString];
[request setPredicate:predicate];
if(matchingData.count <= 0){
self.label.text = #"doesnt exist";}
else {
NSString *string;
for (NSString *obj in matchingData){
string = [obj valueForKey:#"restraunt_name"];
}
self.label.text = [NSString stringWithFormat:#" %#", string] ;
}
}
There are several things wrong in this code. For instance, "textString" is not a string, it's one of your Restraunt entities. Also, the for loop you have in the else clause just loops through all you entities (making your random selection irrelevant), so "string" will be the last restaurant name in your data. Your code should look something like this,
- (IBAction)randomize{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Restraunt"];
NSError *error;
NSArray *matchingData = [context executeFetchRequest:request error:&error];
int randomNumber = arc4random() %[matchingData count];
NSManagedObject *aRestraunt = matchingData[randomNumber]; // aRestraunt might not be an NSManagedObject depending on whether you've subclassed NSManagedObject or not -- if you have, then that should probably be Restraunt instead.
self.label.text = [aRestaurant valueForKey:#"restraunt_name"];
}
After Edit:
The answer I provided above was basically a "fix up" of your code to make it work. However, it's not the most efficient way to get a single random object. There's no need to fetch all the objects into memory just to get one. Instead you can get the count of your records as Hal Mueller suggested in his comment, then use the fetchOffset in combination with the fetchLimit to get one record,
NSError *error;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Restraunt"];
NSUInteger count = [context countForFetchRequest:request error:&error];
if (! error) {
NSUInteger rand = arc4random_uniform() % count;
[request setFetchOffset:rand];
[request setFetchLimit:1];
NSError *fetchError;
NSManagedObject *aRestraunt = [[context executeFetchRequest:request error:&fetchError] lastObject];
if (! fetchError) {
self.label.text = [aRestaurant valueForKey:#"restraunt_name"];
}else{
NSLog(#"There was an error in fetching a Restraunt entity");
}
Related
I am trying to do my Core-data operation on background context.
I insert all my data on the background context but when I fetch on main thread, it doesnt show any data which I already inserted.
I dont know, where am I doing the mistake...:(
Any help appreciate.
This Is what I have tried for insert and the second one is for fetch the data.
- (void)insertNameAndSurnameInDataBase:(NSString *)name surname:(NSString *)surname numbers:(NSArray *)numbers labels:(NSArray *)labels {
AppDelegate *appDel=(AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *saveObjectContext = [appDel saveManagedObjectContext];
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.parentContext = saveObjectContext;
[bgContext performBlockAndWait:^{
NSError *error;
NameAndSurname *nameAndSurname = [NSEntityDescription insertNewObjectForEntityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
NSString *nSurname = [NSString stringWithFormat:#"%# %#",name,surname];
if (nSurname.length == 0) {
nameAndSurname.nameSurname = [numbers objectAtIndex:0];
} else {
nameAndSurname.nameSurname = nSurname;
}
if ([bgContext save:&error]) {
} else {
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
// predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nameSurname =[c] %#", nSurname];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *fetchedObjects = [bgContext executeFetchRequest:fetchRequest error:&error];
if([fetchedObjects count] > 0){
[numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
id obj2 = [labels objectAtIndex:idx];
obj = [obj stringByReplacingOccurrencesOfString:#" " withString:#""];
ContactNumber *phoneNumber = [NSEntityDescription insertNewObjectForEntityForName:#"ContactNumber" inManagedObjectContext:bgContext];
phoneNumber.number = obj;
phoneNumber.label = obj2;
phoneNumber.nameAndSurname = fetchedObjects[0];
NSError *error;
if ([bgContext save:&error]) {
} else {
}
}];
}
}];
}
- (NSArray *)fetchNameAndSurname {
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
_managedObjectContext = [appDel managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setSortDescriptors:#[[[NSSortDescriptor alloc] initWithKey:#"nameSurname" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)]]];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
return [fetchedObjects valueForKey:#"nameSurname"];
}
The mistake you are making is to not test the many little pieces of this code which could be not working as expected. To troubleshoot something like this, you must start at the beginning and test one piece at a time. The first issue I see is confusion between managed object contexts…
It appears that the method saveManagedObjectContext is a getter for your main managed object context. In English, save is a verb, so that this method name implies that it is saving some managed object context. If I am correct, you should change the name saveManagedObjectContext to maybe mainMangaedObjectContext.
The statement _managedObjectContext = [appDel managedObjectContext] is quite nonstandard, assigning to an instance variable from what appears to be its getter. If, as usual, _managedObjectContext is the instance variable backing the property managedObjectContext, this statement has no effect.
In your first method you use [appDel saveManagedObjectContext], and in your second method you use [appDel managedObjectContext]. It looks like these should be the same context in order for your fetch to work. Are they?
UPDATE:
As you stated in your comment, that fixes the saving, but now you have a performance problem – saving to the persistent store on disk blocks the user interface, which is what your original code was trying to fix.
This is a solved problem. The solution is that your main-thread context, which interfaces the user, should be a child of your background-thread context which interfaces to the persistent store. It is explained nicely with code in this 2015 blog post by Marcus Zarra. For further reading, Chad Wilken has published a slight variation. Both are written in Objective-C for you :)
I have 2 Entities. User and Mobile. and have created the relationship also.
I want NSPredicate query, while user types the number the data should come from core data with number and that user name.
+(NSArray *)searchWithNumber:(NSString *)string
{
NSManagedObjectContext *context = [[DataManager sharedManager]managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:NSStringFromClass([Mobile class])];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K CONTAINS[c] %#", #"number", string];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [context executeFetchRequest:request error:&error];
User *user;
Mobile *mobile;
NSMutableArray *arrayTempMobile = [[NSMutableArray alloc]init];
for (int i = 0; i < array.count; i++)
{
mobile = (Mobile *)[array objectAtIndex:i];
NSLog(#"%#",mobile.number);
[arrayTempMobile addObject:mobile.number];
}
}
this is the code. so i m getting numbers only. but i want user name too.
You have a relationship between the mobile and the user so you can navigate it to get the name, like:
mobile.user.name
I want to update the first record in the NSManagedObject. What I have here updates all of them which I realise is because I am selecting them all and updating them all using the for but how do I just update the first record?
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Users"];
NSError *errorLoading = nil;
self.users = [context executeFetchRequest:fetchRequestNext error:& errorLoading];
for (NSManagedObject *usersObject in [self users])
{
[usersObject setValue:#"*" forKey:#"currentUser"];
}
NSError *error;
[context save:&error3];
}
From the results (array) you simply select the one at index:0
NSArray *results = [context executeFetchRequest:fetchRequestNext error:& errorLoading];
if (results.count>0) {
NSManagedObject *userObject = results[0];
[usersObject setValue:#"*" forKey:#"currentUser"];
}
NSError *saveError = nil;
[context save:&saveError];
I hope this helps
Kind of like this:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Users"];
fetchRequest.fetchLimit = 1; //Fetch only one object (optional)
NSError *errorLoading = nil;
NSManagedObject *user = [[context executeFetchRequest:fetchRequestNext error:& errorLoading] firstObject];
if (errorLoading) {
//handle error
}
[user setValue:#"*" forKey:#"currentUser"];
NSError *error = nil;
if (![context save:&error]) {
//handle error
}
First of all, if you only need one object, you can set the fetchLimit to 1. It is not required, it's just a small optimization (it will make CoreData stop after fetching the first object). Then you execute the request just like you normally would and get the first object from the resulting array.
Another option is to use the method firstObject defined in NSArray.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Users"];
NSError *errorLoading = nil;
NSArray *users = [context executeFetchRequest:fetchRequestNext error:& errorLoading];
NSManagedObject *singleUser = [users firstObject];
if(singleUser){
[singleUser setValue:#"*" forKey:#"currentUser"];
}
NSError *error;
if([context save:&error]){
}
Hi I am a bit noob whit core-data so I request for your answer, I can pull out data from database whit this functions:
NSManagedObject *selectedObject = [[self fetchedResultsController]
objectAtIndexPath:indexPath];
[[selectedObject valueForKey:#"SOMECOLLUMNNAME"] description];
But I have a really big problems whit deleting from database I have read that I should use NSPredicate I read the tutorial but I do no figure out how can I write a simple
DELETE *
FROM Table t
WHERE t.date == 01.01.2011
for example.
Can You Help me pls
Hmm. I assume the date object is a NSString, not a NSDate.
Try this:
...// your code
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"date == %#", #"01.01.2011"];
[request setPredicate:predicate];
// Execute the fetch -- create a mutable copy of the result.
NSError * error = nil;
NSMutableArray * mutableFetchResults = [[yourManagedObjectContext executeFetchRequest:request error:&error] mutableCopy];
for (int i = 0; i < [mutableFetchResults count]; i++)
{
YourEntity * object = (YourEntity*)[mutableFetchResults objectAtIndex:i];
[yourManagedObjectContext deleteObject:object];
}
[mutableFetchResults release];
Pls ask if you need more help.
Thanks a lot Elias
Here is my nearly final code:
-(void)deleteLastDate{
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:0];
NSString *startDate=[[NSString alloc]initWithString:#"01.01.2011"];//[[managedObject valueForKey:#"date"] description]];
NSManagedObjectContext *moc = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(date == %#)", startDate];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:[entity name] inManagedObjectContext:moc]];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
int aegedInt = [results count];
for (int i = 0; i<aegedInt; i++) {
[moc deleteObject:[results objectAtIndex:i]];
}
}
I hope it will help people dealing whit same problem
Thanks again Elias
I have three Classes for which I am extending NSManagedObject (I know this isn't required but I wanted the property notation). Theses Classes are QuestionSet, Question and Answer. My data model is setup so that there's a many to many between them like so: QuestionSet <<->> Question <<->> Answer. I load up everything from a server and it save successfully meaning I look in the .db file and it's all how I expect, if I immediately NSLog the array questionsets they look great and if I go take a quiz it works great.
However if I NSLog that array anywhere but right after where I load them from the server, it crashes my app. The quiz still runs fine so I know the data is in there and in the expected format. Does this have something to do with Core Data clearing out space and then my relationships don't 'lazy loaded' when I'm just trying to log them? Sorry, still wrapping my head around core data.
This bit works, I load from the server and at the bottom I 'loadTrivia' and log what I get. The issue comes if at sometime later during the course of the app execution, an NSLog of the questionsets will fail.
- (void)loadQuestionSetFromNetworkWithID:(NSNumber *)p_id andTitle:(NSString *)p_title
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:kTriviaQuestionSetURL, [p_id intValue]]]];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSArray *questions = [[[sbjson objectWithString:json_string error:nil] valueForKeyPath:#"Q"] objectAtIndex:0];
NSError *error;
NSFetchRequest *questionsetRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *questionsetEntity = [NSEntityDescription entityForName:#"QuestionSet" inManagedObjectContext:[self managedObjectContext]];
NSPredicate *questionsetPredicate = [NSPredicate predicateWithFormat:#"id = %d", [p_id intValue]];
[questionsetRequest setEntity:questionsetEntity];
[questionsetRequest setPredicate:questionsetPredicate];
QuestionSet *qs = nil;
NSArray *questionSetObjects = [[self managedObjectContext] executeFetchRequest:questionsetRequest error:&error];
if (questionSetObjects == nil){
// Handle errors
}
if ([questionSetObjects count] > 0)
qs = [questionSetObjects objectAtIndex:0];
else
qs = [NSEntityDescription insertNewObjectForEntityForName:#"QuestionSet" inManagedObjectContext:[self managedObjectContext]];
qs.id = p_id;
qs.title = p_title;
for (int i = 0; i < [questions count]; i++)
{
NSFetchRequest *questionRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *questionEntity = [NSEntityDescription entityForName:#"Question" inManagedObjectContext:[self managedObjectContext]];
NSPredicate *questionPredicate = [NSPredicate predicateWithFormat:#"(id = %d)", [[[questions objectAtIndex:i] objectForKey:#"id"] intValue]];
[questionRequest setEntity:questionEntity];
[questionRequest setPredicate:questionPredicate];
Question *q = nil;
NSArray *questionObjects = [[self managedObjectContext] executeFetchRequest:questionRequest error:&error];
if (questionObjects == nil){
// Handle errors
}
if ([questionObjects count] > 0)
q = [questionObjects objectAtIndex:0];
else
q = [NSEntityDescription insertNewObjectForEntityForName:#"Question" inManagedObjectContext:[self managedObjectContext]];
q.id = [NSNumber numberWithInt:[[[questions objectAtIndex:i] objectForKey:#"id"] intValue]];
q.question = [[questions objectAtIndex:i] objectForKey:#"text"];
q.answer = [NSNumber numberWithInt:[[[questions objectAtIndex:i] objectForKey:#"ca"] intValue]];
for (int j = 0; j < [[[questions objectAtIndex:i] objectForKey:#"A"] count]; j++)
{
NSFetchRequest *answerRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *answerEntity = [NSEntityDescription entityForName:#"Answer" inManagedObjectContext:[self managedObjectContext]];
NSPredicate *answerPredicate = [NSPredicate predicateWithFormat:#"(id = %d)", [[[[[questions objectAtIndex:i] objectForKey:#"A"] objectAtIndex:j] objectForKey:#"id"] intValue]];
[answerRequest setEntity:answerEntity];
[answerRequest setPredicate:answerPredicate];
Answer *a = nil;
NSArray *answerObjects = [[self managedObjectContext] executeFetchRequest:answerRequest error:&error];
if (answerObjects == nil){
// Handle errors
}
if ([answerObjects count] > 0)
a = [answerObjects objectAtIndex:0];
else
a = [NSEntityDescription insertNewObjectForEntityForName:#"Answer" inManagedObjectContext:[self managedObjectContext]];
a.id = [NSNumber numberWithInt:[[[[[questions objectAtIndex:i] objectForKey:#"A"] objectAtIndex:j] objectForKey:#"id"] intValue]];
a.answer = [[[[questions objectAtIndex:i] objectForKey:#"A"] objectAtIndex:j] objectForKey:#"text"];
a.questions = [a.questions setByAddingObject:q];
q.answers = [q.answers setByAddingObject:a];
[answerRequest release];
}
q.questionsets = [q.questionsets setByAddingObject:qs];
qs.questions = [qs.questions setByAddingObject:q];
[questionRequest release];
}
[questionsetRequest release];
[[self managedObjectContext] save:&error];
[json_string release];
[self loadTrivia];
NSLog(#"After Load: %#", self.questionsets);
}
This function fetches all the QuestionSet objects from the db and stores the array in an ivar.
- (BOOL)loadTrivia
{
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"QuestionSet" inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
self.questionsets = [[self managedObjectContext] executeFetchRequest:request error:&error];
return YES;
}
Just for completeness, here are my description methods in my classes
from QuestionSet.h
- (NSString *)description
{
return [NSString stringWithFormat:#"\
\n\tid: %#\
\n\ttitle: %#\
\n\tquestions: %#\n",
self.id,
self.title,
self.questions];
}
from Question.h
- (NSString *)description
{
return [NSString stringWithFormat:#"\
\n\tid: %#\
\n\tanswer: %#\
\n\tquestion: %#\
\n\tanswers: %#\n",
self.id,
self.answer,
self.question,
self.answers];
}
from Answer.h
- (NSString *)description
{
return [NSString stringWithFormat:#"\
\n\tid: %#\
\n\tanswer: %#\n",
self.id,
self.answer];
}
You shouldn't subclass the description method (Link):
Although the description method does not cause a fault to fire, if you implement a custom description method that accesses the object’s persistent properties, this will cause a fault to fire. You are strongly discouraged from overriding description in this way.
I tested your loadTrivia code (with a different entityName) on my app and it worked fine.
Did you have a look at the NSError (you should initialize it with =nil):
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
Also you should release the NSFetchRequest after execution in - (BOOL)loadTrivia.