Get all results of a NSFetchRequest in an NSArray - ios

In my app I do this thing to abtain all specific value from an entity
NSManagedObjectContext *context = [[self sharedAppDelegate] managedObjectContext];
NSError *error = nil;
NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:#"Structure"];
[req setPropertiesToFetch:#[#"id_str"]];
[req setResultType:NSDictionaryResultType];
NSArray *id_str_BD = [context executeFetchRequest:req error:&error];
int this way I obtain a NSArray of NSDictionaryies but I want directly an array of values.
What's a fast way to obtain it without loops?

I agree with Tom. What values?
Anyway, you can accomplish it through KVC. For example,
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Structure"];
[fetchRequest setPropertiesToFetch:#[#"id_str"]];
NSArray* results = [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
NSArray* ids = [results valueForKey:#"id_str"];
NSLog(#"%#", ids);
NOTES
Do not pass nil for the error. ALWAYS check for a possible error (for the sake of simplicity I don't use it in this code snippet)
I would rename id_str to idStructure

Related

Update NSManagedObjectContext

Please consider the method at the end of this question. It attempts to delete all records from a CoreData entity.
The first -outcommented- part works fine: it deletes everything from my context, and then saves the context.
The second, non-commented part doesn't seem to work:
As a test I do
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"myEntity"];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"Fetch request returns %lu objects", (unsigned long)[results count]);
When I add records, and delete all records with the method below, the amount of records only increase.
As far as I understand, it actually works, but the context is not aware of it (yet).
So, therefore, in order to get a reliable context, I should update the context with
[NSManagedObjectContext mergeChangesFromRemoteContextSave:<#(nonnull NSDictionary *)#> intoContexts:self.managedObjectContext];
However, I don't have a clue what I should enter in the (nonnull NSDictionary *) part.
BTW: I only want to use the NSBatchDeleteRequest because I assume it is faster than iterating through all records.
- (void)deleteAllEntities:(NSString *)nameEntity
{
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
self.managedObjectContext= [appDelegate managedObjectContext];
/*
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
[fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError *error;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[self.managedObjectContext deleteObject:object];
}
error = nil;
[self.managedObjectContext save:&error];
*/
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
NSError *deleteError = nil;
}

Delete specific data from specific entity from core data in objective c?

I have two entity in my core data named "History" and "Favorite". i want to delete some specific data like "Dhaka" from "History" entity. How to delete this. Thanks in advance.
Hope it will help you.
NSString* aType = #"History";
NSString *tp = #"Dhaka";
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:aType inManagedObjectContext:self.managedObjectContext]];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"(type == %#)",tp ]; //where type is the attribute
[request setPredicate:predicate];
NSArray* ar = [self.managedObjectContext executeFetchRequest:request error:nil];
NSLog(#"############# Data from DB : %#",ar);
for(id anObj in ar){
[managedObjectContext deleteObject:anObj];
}
[self.managedObjectContext save:nil];
predicate=nil;
try this:
objAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = [objAppDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"History"];
NSArray *recordArray =[[NSArray alloc]initWithArray:[context executeFetchRequest:request error:nil]];
objDataClass = [recordArray objectAtIndex:0]; // here objDataClass is a instance of DataClass derived from NSObject which define column name
[context deleteObject:objDataClass]; // Delete particular data with index
if ([context hasChanges])
{
[context save:nil];
}
else
{
NSLog(#"Object Deleted.....");
}
Hope it will help you. :)

Core Data fetch request optimization

I'm developing and application which needs to check if a string has been saved to the database or not. This may seem an easy operation but it needs half a second to return any response which I think is quite a lot. My question is if there is any way to reduce that time. Thanks for your interest.
This is my current code:
- (BOOL) isDeleted:(int)i {
NSString *value = [NSString stringWithFormat:#"deleted.*.%i", i];
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSString *entityName = #"Deleted";
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(deletedpics like %#)", value];
[request setPredicate:pred];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
BOOL returnBool = [objects count] >= 1 ? YES : NO;
return returnBool;
}
One optimization is to replace
NSArray *objects = [context executeFetchRequest:request error:&error];
by
[request setFetchLimit:1];
NSUInteger count = [context countForFetchRequest:request error:&error];
if you only want to check for the existence of objects.
But I assume that the main performance problem is the wildcard search with LIKE, which is slower than searching with ==.
Instead of storing the status and the picture number in one
attribute as deleted.<status>.<picturenumber> (as you wrote in a comment) it would probably be better to use separate attributes status and picturenumber in the entity.
Then you can search for a picture number with the predicate
[NSPredicate predicateWithFormat:#"picturenumber == %d", i];
which should be much faster. If necessary, you can additionally index that property (as #flexaddicted mentioned in a comment).

Core Data - how to check if user exists in database?

So I'm trying to create a simple (if such one exists) login process for my app, and I am getting an error with the below block of code.
NSManagedObject *context = _managedObjectContext;
NSFetchRequest *request= [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Account" inManagedObjectContext:context];
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"username==%#",self.textFieldUsername.text];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error = nil;
// Below line is giving me error
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if (array != nil) {
NSUInteger count = [array count]; // may be 0 if the object has been deleted.
NSLog(#"Username may exist, %#",count);
}
else {
NSLog(#"Username does not exist.");
}
`
The above code right now is ran when the user clicks the Login button.
Xcode is giving me an error of:
No visible #interface for 'NSManagedObject' declares the selector 'executeFetchRequest:error.'
When I read the above statement it just seems greek to me. The name of the file I am working on is ViewControllerWelcome.m and can be viewed in it's entirety at the following link. If it matters, I am using the stock boilerplate core data code.
Bonus: How do I get objective-c code highlighting when I post on here (SO)?
Change:
NSManagedObject *context = _managedObjectContext;
To
NSManagedObjectContext *context = _managedObjectContext;
and
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
should be
NSArray *array = [context executeFetchRequest:request error:&error];
P.S.
If you want the username search to be case insensitive use "username ==[c] %#" for the predicate...

Pro Core Data book, example code question

I'm learning Core Data with the help of the book. There is a code:
- (void)loadData {
// Pull the movies. If we have 200, assume our db is set up.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Movie"
inManagedObjectContext:context]];
NSArray *results = [context executeFetchRequest:request error:nil];
if ([results count] != 200) {
// Add 200 actors, movies, and studios
for (int i = 1; i <= 200; i++) {
[self insertObjectForName:#"Actor" withName:
[NSString stringWithFormat: #"Actor %d", i]];
[self insertObjectForName:#"Movie" withName:
[NSString stringWithFormat: #"Movie %d", i]];
[self insertObjectForName:#"Studio" withName:
[NSString stringWithFormat: #"Studio %d", i]];
}
// Relate all the actors and all the studios to all the movies
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Movie"
inManagedObjectContext:context]];
NSArray *results = [context executeFetchRequest:request error:nil];
for (NSManagedObject *movie in results) {
[request setEntity:[NSEntityDescription entityForName:#"Actor"
inManagedObjectContext:context]];
NSArray *actors = [context executeFetchRequest:request error:nil];
NSMutableSet *set = [movie mutableSetValueForKey:#"actors"];
[set addObjectsFromArray:actors];
[request setEntity:[NSEntityDescription entityForName:#"Studio"
inManagedObjectContext:context]];
NSArray *studios = [context executeFetchRequest:request error:nil];
set = [movie mutableSetValueForKey:#"studios"];
[set addObjectsFromArray:studios];
}
}
[request release];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
The main question is: is it necessary to renew the context pointer, if there were any changes in that context?
What I mean: I get the pointer to the context at the beginning of the method, next in the loop I take this context and insert managed objects there (-insertObjectForName:withName:). Then I see this renewal of the context pointer and have that question: is it a rule of some kind, and I should act the same, or its just not-so-neat code example? Why can't I use the old pointer?
---Edit--- One more question: is that a legal initialization of a request here in the code:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
...
NSFetchRequest *request = [[NSFetchRequest alloc] init];
...
[request release];
Two allocs and only one release?
I also have this book and looked it up.
Clearly seems like a typo to me and doesn't really make much sense.
Just ignore that line and continue - it should work fine without.
In my opinion
its just not-so-neat code example
About your second question: There have to also two releases! Otherwise you have a leak.
That's some seriously ugly code.
This:
NSManagedObjectContext *context = [self managedObjectContext];
... is probably just because they want to be able to write context in a method call instead of self.managedObjectContext.
Using:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
... twice is allowed but it is lazy bad practice. Any variable should be named only once in a scope. In fact, the compiler will generate a warning with this code. It will leak because every init must be balanced by a release.

Resources