Xcode 8.1 core data fetching specific properties - xcode

I am trying to fetch a specific property from Core Data using the following code:
NSFetchRequest *test = [[NSFetchRequest alloc] init];
test.entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:self.currentMainContext];
test.predicate = [NSPredicate predicateWithFormat:#"TRUEPREDICATE"];
test.resultType = NSDictionaryResultType;
test.returnsDistinctResults = YES;
test.propertiesToFetch = #[#"property"];
NSArray *results = [self.currentMainContext executeFetchRequest:test error:error];
but I get an empty array back.
If I comment out test.resultType = NSDictionaryResultType; then I get back an array of all the entities in my database as expected. What is it about the NSDictionaryResultType that is wrong?

Fetch request with resultType of NSDictionaryResultType has one peculiarity.
Documentation for includesPendingChanges states that:
If the value is NO, the fetch request doesn't check unsaved changes and only returns objects that matched the predicate in the persistent store.
A value of YES is not supported in conjunction with the result type NSDictionaryResultType, including calculation of aggregate results (such as max and min). For dictionaries, the array returned from the fetch reflects the current state in the persistent store, and does not take into account any pending changes, insertions, or deletions in the context.
So make sure that you have saved your changes, or use NSManagedObjectResultType.

Related

get the first entry in an entity on Core Data

I was wondering how to get the first item from an entity in core data without using fetch?
Reason i asked is because i placed the settings file onto core data instead of creating a separate file to hold the values. Because when ever the app is updated, the separate file gets blown off and core data is migrated. if i placed the user's settings onto a file, it is not preserved when the app is updated. Core data is preserved (give or take some hoops).
I know how to fetch but is there any other way (easier way) to just get the 1st entry on the core data entity?
I am afraid you have to go through the motions of fetching. You can make it very efficient by setting the fetchLimit to 1. With the available convenience methods it is really very short, both in Swift and Objective-C.
var request = NSFetchRequest(entityName:"Event")
request.fetchLimit = 1
let result = context.executeFetchRequest(request, error:nil) as [Event]
if countElements(result) == 1 {
let event = result.first! as Event
// do something with event
}
or
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
request.fetchLimit = 1;
NSArray *result = [context executeFetchRequest:request error:nil];
if (result.count) {
Event *event = result.firstObject;
// do something with event
}

Fetched Results Controller objectAtIndexPath without using TableView

I have a Core Data financial app that needs to accumulate sales quantities contained in the Trans Entity for each product and then update the totals into the appropriate attribute of the Product Entity.
I am able to achieve this by nesting a for (transArray) inside and tableView (product).
However I need to sort and format the tableView based on the results first.
General Question: Can fetched results be used without tableViews?
- (void)calculateAmounts {
NSIndexPath *indexPath=0;
for (Product *product in self.fetchedResultsController.fetchedObjects){ // All product records
selectedProduct = [self.fetchedResultsController objectAtIndexPath:indexPath];
// >>>>>NSLog shows correct number of object, however selectedProduct # Index Path Are NULL
for (id product1 in transProductArray) { // An array of all of the trans for product
if ((NSNull *)product1 == [NSNull null]) {
}
else if ([product1 isEqualToString:selectedProduct]) {
float qty = [#"1" floatValue];
NSNumber *numQty=[NSNumber numberWithFloat:qty]; // Update quantity sold in product by 1
NSNumber *quantity = [NSNumber numberWithFloat:([selectedProduct.quantitySold floatValue] + [numQty floatValue])];
selectedProduct.quantitySold = quantity;
[self.product.managedObjectContext save:nil];
}
} // Next Trans
} // Next Product
}
This is a good question. Not (clearly) knowing the implementation, I will bet it is possible to use fetched results outside a tableview. However, the Overview of NSFetchedResultsController documentation has this as the first line:
You use a fetched results controller to efficiently manage the results
returned from a Core Data fetch request to provide data for a
UITableView object.
While table views can be used in several ways, fetched results
controllers are primarily intended to assist you with a master list
view.
This infers that the two objects are linked for efficiency and ease of use. Plus, there are monitors under-the-hood to watch for changes, etc.
What you may be looking for is a 'plain-old' NSFetchRequest. A tutorial.
Hi I am not to sure whether you still require an answer to this however I had the same issue myself. I overcame as described in my own question on the subject.
Display multiple core data entities objects in 1 Non-Table View Controller
I inserted a small tableview into my VC and set it to alpha and used the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to enable displaying details in labels.
This was the only way I could overcome it. Hope this helps.
Thanks for the answer. Like a lot of things the answer was too easy! It's about the fetch, not the tableview. Just do a fetch into an array, then loop though the array to accumulate your values: Also useful for finding a specific object or output to a CSV.
NSError *error;
NSNumber *total=0;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"TransDetail" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (TransDetail *trans in fetchedObjects) {
total = total + trans.amount;
}

Understanding Core Data Queries(searching data for a unique attribute)

Since I am coming from those programmers who have used sqlite extensively, perhaps I am just having a hard time grasping how Core Data manages to-many relationships.
For my game I have a simple database schema on paper.
Entity: Level - this will be the table that has all information about each game level
Attributes: levelNumber(String), simply the level number
: levelTime(String), the amount of time you have to finish the level(this time will vary with the different levels)
: levelContent(String), a list of items for that level(and that level only) separated by commas
: levelMapping(String), how the content is layed out(specific for a unique level)
So basically in core data i want to set up the database so i can say in my fetchRequest:
Give me the levelTime, levelContent and levelMapping for Level 1.(or
whatever level i want)
How would i set up my relationships so that i can make this type of fetchRequest?
Also I already have all the data ready and know what it is in advance. Is there any way to populate the entity and its attributes within XCode?
As you've described it, it's a single Core Data entity, called Level that has four string attributes. Since there's just the one entity, there are no relationships. You'd create the one entity and add properties so that it looks just like you've described it above:
Getting just one Level is basic Core Data fetching:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Level"];
NSString *levelNumber = #"1";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"levelNumber = %#", levelNumber];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:&error];
NSManagedObject *level = nil;
if ([results count] > 0) {
level = [results objectAtIndex:0];
}
// Use level...
If it was me I'd use one of the numeric types for levelNumber, but maybe you have some reason to use a string there. I'd also probably break levelContent into a separate entity, because (a) comma delimited strings are ugly, no matter how you slice 'em, and (b) you might well want the items to have more attributes, and a separate entity would hold those.

Core Data - get/create NSManagedObject performance

I'm creating an iphone/ipad app that basically reads XML documents and creates tableviews from objects created based on the xml. The xml represents a 'level' in a filesystem. Its basically a browser.
Each time i parse the xml documents i update the filesystem which is mirrored in a core-data sqllite database. For each "File" encountered in the xml i attempt to get the NSManagedObject associated with it.
The problem is this function which i use to get/create either a new blank entity or get the existing one from database.
+(File*)getOrCreateFile:(NSString*)remotePath
context:(NSManagedObjectContext*)context
{
struct timeval start,end,res;
gettimeofday(&start,NULL);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"File" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:1];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"remotePath == %#",remotePath];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
File *f;
if ([items count] == 0)
f = (File*)[NSEntityDescription insertNewObjectForEntityForName:#"File" inManagedObjectContext:context];
else
f = (File*)[items objectAtIndex:0];
gettimeofday(&end, NULL);
[JFS timeval_subtract:&res x:&end y:&start];
count++;
elapsed += res.tv_usec;
return f;
}
For eksample, if i'm parsing a document with 200ish files the total time on a iPhone 3G is about 4 seconds. 3 of those seconds are spent in this function getting the objets from core data.
RemotePath is a unique string of variable length and indexed in the sqllite database.
What am i doing wrong here? or.. what could i do better/different to improve performance.
Executing fetches is somewhat expensive in Core Data, though the Core Data engineers have done some amazing work to keep this hit minimal. Thus, you may be able to improve things slightly by running a fetch to return multiple items at once. For example, batch the remotePaths and fetch with a predicate such as
[NSPredicate predicateWithFormat:#"remotePath IN %#", paths];
where paths is a collection of possible paths.
From the results, you can do the searches in-memory to determine if a particular path is present.
Fundamentally, however, doing fetches against strings (even if indexed) is an expensive operation. There may not be much you can do. Consider fetching against non-string attributes, perhaps by hasing the path and saving the hash in the entity as well. You'll get back a (potentially) larger result set which you could then search in memory for string equality.
Finally, do not make any changes without some performance data. Profile, profile, profile.

How do I get Core Data to use my own NSManagedObjectID URI scheme?

I am writing an app that connects to a database to fetch data. Since the fetching is expensive and the data is generally unchanging, I'm using CoreData to cache the results so that I can do fast, local queries.
From the database, for each type, there is a string property that is guaranteed to be unique. In fact, there is a URI scheme for the database which is a unique address for each item.
The URL scheme is very basic along the lines of:
ngaobject://<server_license_id>/<type>/<identifier>
I'd like to be able to use this in CoreData as well. I've made a method to fetch a single item from the CoreData store:
-(NSFetchRequest*)fetchRequestForType:(NSString*)typeName identifier:(NSString*)identifier
{
NSFetchRequest * fetchRequest = [self fetchRequestForType:typeName];
[fetchRequest setFetchLimit:1];
NSString * identifierProperty = [self identifierPropertyNameForObjectType:typeName];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"%K == %#", identifierProperty, identifier];
[fetchRequest setPredicate:predicate];
return fetchRequest;
}
-(NGAObject*)objectWithType:(NSString*)typeName
identifier:(NSString*)identifier
{
// First try to retrieve it from the cache
NSAssert1( (identifier != nil), #"Request to create nil-name object of type %#", typeName );
NSFetchRequest * fetchRequest = [self fetchRequestForType:typeName identifier:identifier];
if ( !fetchRequest )
return nil;
NSError * error = nil;
NSArray * fetchResults = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if ( !fetchResults )
{
NSLog(#"%#", error);
[NSApp presentError:error];
return nil;
}
if ( [fetchResults count] )
return [fetchResults objectAtIndex:0];
return nil;
}
When I retrieve an item from the server, I want to first get a reference to it in the cache and if it's there, update it. If it's not, create a new one.
Since I'm getting back thousands of objects from the server, performing a fetch for a single object for which I know a unique ID brings my machine to a crawl.
Instead, what I'm doing is pre-loading all the objects of a type, then creating a dictionary of identifiers->object, then processing the thousands of objects for that type by running it through the dictionary. This works fine, but is awkward.
Could I not write a method that takes the type/identifier combo and get a single object from CoreData without having to execute a lengthy fetch request?
It seems there is a solution if I can get CoreData to use my own URI specification. I could then call -(NSManagedObjectID*)managedObjectIDForURIRepresentation:(NSURL*)url on the persistent store coordinator.
So, the question is, how can I get CoreData to use my URI scheme? How can I make CoreData use my own unique identifiers?
You can't make Core Data use a custom URI scheme. The URI scheme is hardcoded into Core Data such that the URI can be decoded to locate particular data in a particular store in a particular apps on a particular piece of hardware. If the URI was customizable, that system would break down.
Fetching object singularly is what is killing you. Instead you need to batch fetch all objects whose customID matches those provided by the server. The easiest way to that is to use the IN predicate operator:
NSArray *customIDs=//... array of customIDs provided by server
NSPredicate *p;
p=[NSPredicate predicateWithFormat: #"customIdAtrribute IN %#", customIDs];
This will return all existing objects that you can ignore.
Alternatively, you could
Do a fetch on just the customID property by setting the fetch's propertiesToFetch to the customID attribute.
Set the fetch result type to dictionary.
Use the above predicate.
You will get an array of one key dictionaries returned with the customID as each value.
Convert the dictionary to an array of values e.g cachedIDs
Convert customIDs above to a mutable array.
Filter the customIDs array using the predicate, #"NOT (SELF IN %#)", cachedIDs"
The filtered customIDs array will now only contain the customID values NOT cached in Core Data.
You can create managed objects for only the new ids.
(This is how you use a filter predicate if you are unfamilar with it.)
NSMutableArray *f=[NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",nil];
NSArray *g=[NSArray arrayWithObjects:#"5",#"6",nil];
[f filterUsingPredicate:[NSPredicate predicateWithFormat:#"NOT (SELF IN %#)",g]];
NSLog(#"f=%#",f);
...which outputs:
f=(
1,
2,
3,
4
)
Are all the fields which you are using for unique-ID lookup marked as "Indexed" in the CoreData designer? If that has been done then the CoreData fetches shouldn't be lengthy ...

Resources