NSFetchRequest based on array property size - ios

I have a NSManagedObject which has a (transformable) NSArray property arrayProperty.
I would like to execute a fetch request filtering for objects whose property size is less than 5. I tried
[NSPredicate predicateWithFormat:#"arrayProperty.#count <= 5"]
and
[NSPredicate predicateWithFormat:#"arrayProperty[SIZE] <= 5"]
but none of them work (giving Unsupported function expression count and Unsupported function [SIZE]).
Is there a way to achieve this?

This kind of thing isn't possible with transformable properties. Transformable are convenient for storing data, but once they're stored, that's all they are-- a bunch of bytes with no information about the data structure that created them. SQLite doesn't know that it's an array, so it's not able to do things like count how many items are in the array.
If you use transformable properties, you lose any ability to filter based on them, except for extremely basic tests like whether the value is nil.
If you need to do this kind of filtering, you'll need to consider how to change your data model. One possibility would be that instead of saving an array, create a new entity with a to-many relationship to store the values.

Related

Random fetch from core data

I have n number of object of an entity.
I want to fetch 30, random object from the same entity.
I'm using core data, swift 3. Could anybody help me to solve this problem?
Thanks,
This is going to require an extra step, because Core Data doesn't have any built-in support for a random selection. You'll need to have some unique attribute, select your own random subset of values for that attribute, and then get the managed objects with those values.
First, you need a managed object property that has unique values. Any property will do, but numeric properties will work faster. Let's say for example you have an integer property called myUniqueID that has unique values.
Do a fetch request to get all current values of this property. You only want values of this specific property, so set the fetch result type to NSFetchRequestResultType.dictionaryResultType and set the fetch request's propertiesToFetch to include only myUniqueID. The result will be an array of dictionaries, each containing a single value of myUniqueID.
Add your own logic to choose 30 random values from this result. Collect them in an array.
Do a second fetch request, this time fetching managed objects instead of dictionaries. If your array of random IDs is called uniqueIDArray, use a predicate of something like NSPredicate(format: "myUniqueID in %#", uniqueIDArray)

iOS - Reorder Items in CoreData

I have a tableView and an array locationsArray. The locations array is filled on viewDidLoad from data in my CoreData.
I have implemented the reorder methods on the tableView and that works fine however I can't figure out how to save the reordered array back into CoreData. I've tried deleting everything in CoreData and just saving the new reorderedArray. Although this is obviously very costly resource wise it seems to just load the elements back into the array in a seemingly random order.
Is there an easy way to do this that I am missing or can you not store things in CoreData in a certain order?
Strictly saying, it is impossible to store objects in certain order. You can only set the order for retrieved objects
When you retrieve locationsArray from CoreData, you have no any guaranties about their order.
The only way - add field to your Managed Object that will be used to order your locations.
Than, on retrieval, you'll be able to use locationsArray = [locationsArray sortedArrayUsing<#WhateverYouNeed#>];.
You can, also, achieve this by using sortDescriptors property of NSFetchRequest. It will provide you with desired behaviour from the box. The only downside of it, that your NSSortDescriptor in that case cannot be defined by block, so if you ordering is using some complex approach, you'll need to sort the result with already fetched data
use the predicate to sort the array or after fetching the array from coredata sort it using
NSArray *sortedlocationarray = [filteredlocationarray sortedArrayUsingDescriptors:#[sortDescriptor]];
where filteredlocationarray is from coredata.

FetchResultsController sorting issue

I have an NSFetchResultsController initialised with an NSSortDescriptor that sorts according to the localizedCaseInsensitiveCompare: method.
Entities are sorted on their last name, however some of them don't specify their last name and hence, the char 170 (in ASCII) is set as their last name (don't ask me why, the source code is done that way and I rather don't change it). When sorted, these persons "without last name" are displayed at the top of the table view. I would like them to be at the end of it.
Seems that it is not possible to use specific NSSortDescriptor in my case (i'm using a FRC), what are my options here ?
[EDIT]
Comparator blocks or custom comparator methods won't work with NSFetchResultsController
Your options are limited as the sort descriptor needs to be able to work with an SQLite data store.
You should edit the data model so you have an attribute that you can sort on. Exactly how you calculate the value is up to you, but the attribute can't be temporary. I would suggest that you either:
Implement willSave in your NSManagedObject subclass to create / update the sort data
Or, make your sort data a dependent key (using KVO, again in your NSManagedObject subclass)
Your sort data could be as simple as:
self.sortValue = (self.lastName == 170 in ASCII ? #"ZZZZZZZZZZZZZZZ" : lastName)

Creating sections with NSFetchedResultsController, on the fly

I'm using NSFetchedResultsController (NSFRC) to display information in a UITableView. I'm trying to create the option for the user to sort the cells in sections as opposed to alphabetically. The problem is, the sections would then be determined using downloaded information. On top of this the section for each item will be changing relatively often so I don't want to save the section. I have noticed the mention of transient attributes, in my research of similar problems, but i've never used these before I'm not sure if I can use them baring in mind that all the calculations are done once the data has already been loaded, and I also want this solution to be compatible with my previous Core Data database. Also I'm not particularly great at Core Data, (nor Objective-C at that!) so I'm not entirely sure how I'd go about doing this.
So here's what I want to go for if we're using transient attributes (this next bit is theoretical as I don't know if transient attributes are the correct way forward). I would like 4 possible sections, 0-3 (I'll rename them using the TableView delegate to get around sorting problems). When the calculations are done, each cell will be assigned the transient attribute (if needed, the default section would be 2). I hope this all makes sense.
Right, now for some theoretical code. First I create the transient property in the Data Model screen-thing, and make it transient by checking the transient check box... Sounds simple enough.
In the code for the calculations in willDisplayCell (needs to be done in wDC for a couple of reasons), the entity could be saved like this:
MyEntity *myEntity = [self.fetchedResultsController objectAtIndexPath:indexPath];
myEntity.sectionTransientProperty = 2;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Error: %#", error);
FATAL_CORE_DATA_ERROR(error);
return;
}
Done, right? Is that how we assign a value to a transient property?
Then I change the sorting option in NSFRC when I alloc it:
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sectionTransientProperty"
cacheName:#"MyEntity"];
How are we doing, what else do I need to do? Or have I got this so horribly wrong I should just give up on Core Data and NSFRC? If you guys could help guide me through this I'd really appreciate it. If you need me to post any more code I would be happy to.
Regards,
Mike
If you want an FRC with sections, you have to add a sort descriptor to the fetch request, and that sort descriptor cannot be based on transient attributes.
See the documentation of initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:`:
If the controller generates sections, the first sort descriptor in
the array is used to group the objects into sections; its key must
either be the same as sectionNameKeyPath or the relative ordering
using its key must match that using sectionNameKeyPath.
and Fetch Predicates and Sort Descriptors in the "Core Data Programming Guide":
The SQL store, on the other hand, compiles the predicate and sort
descriptors to SQL and evaluates the result in the database itself.
This is done primarily for performance, but it means that evaluation
happens in a non-Cocoa environment, and so sort descriptors (or
predicates) that rely on Cocoa cannot work. The supported sort
selectors are ...
In addition you cannot sort on transient properties using the SQLite store.
This means that you cannot create sections purely on transient attributes. You need a persistent attribute that creates the ordering for the sections.
UPDATE: A typical use of a transient attribute as sectionNameKeyPath is: Your objects have a "timeStamp" attribute, and you want to group the objects into sections with one section per month (see the DateSectionTitles sample code from the iOS Developer Library). In this case you have
a persistent attribute "timeStamp",
use "timeStamp" as first sort descriptor for the fetch request,
a transient attribute "sectionIdentifier" which is used as sectionNameKeyPath. "sectionIdentifier" is calculated from "timeStamp" and returns a string representing the year and the month of the timestamp, e.g. "2013-01".
The first thing the FRC does is to sort all fetched objects according to the "timeStamp" attribute. Then the objects are grouped into sections according to the "sectionIdentifier" attribute.
So for a FRC to group the objects into sections you really need a persistent attribute. The easiest solution would be to add a persistent attribute "sectionNumber" to your entity, and use that for "sectionNameKeyPath" and for the first sort descriptor.

NSSortDescriptor with a function

I only have a limited experience in using NSSortDescriptor.
It was sorting on one key and it worked fine.
But here is what I need now, I have a set of pairs of numbers, for example :
{(2,3), (44,5), (6,17), (33,7) ……(173,21)}
I want to sort the pairs (x,y) according to the value of a given function myfunction(x,y).
There is the trivial idea of making triplets (x,y,z) where z would be the computation of myfunction(x,y) and then sort the set of triplets, but this not what I want.
Is there a proper way to use NSSortDescriptor to do what I need?
Thanks for any information.
Unless you are using this sort descriptor with Core Data using a SQLite store, you could create a transient attribute on the object where the attribute represented z, the computation of your function. You could then sort on that transient attribute and it would produce the results you want, without storing the z values.
However, if you are using Core Data with a store like SQLite where the entire store contents are not read into memory, you cannot use transient attributes to sort on and you would have to either store your z values actually in the managed objects in order to achieve what you are describing, or read all of your managed objects into an array, and then sort that array of objects on the transient property... which would work OK if you only had a limited number of managed objects, but not so well otherwise.

Resources