Distance group NSFetchedResultsController sections - objective-c

I'm using Core Data for a tableview. My data is ordered by the distance from your current location. What I'd like is to have a new section for items with 5, 10 and 20 miles.
My distance value is stored in the data store as an NSInteger and I get it out using a NSNumber in my object model.
I've done a bit of searching around and found that I need to use the sectionNameKeyPath attribute to make the data sectioned.
My problem is that I don't know the best way to group the data. During my searches I came across either a transient property or using a category of NSNumber to work out which section the item should be in.
Are either of these methods the best way of getting my end result, if so, can anyone provide any details on how to implement it?
Thanks.

You should add a derived attribute to the object (it's not strictly needed in the model), let's name it range. I'd make it a read only property, you can cache the value or not.
When it's 0-5, return 5, 5.x - 10 return 10, etc.
Then set range as your sectionNameKeyPath.

If you want a highly customized section behavior, you need to subclass NSFetchedResultsController to give you the behavior you want. See the NSFetchedResultsController class docs for details.
You will need to subclass in this case because your not looking at a single ordered attribute like the alphabetized first letter of a string attribute but rather a range in which each attribute falls into.
This is a cleaner solution than altering the data model because you can use it to display the data many different ways in many different tableviews without having to muddy up your data model.

Related

Many-to-many relationships with NSFetchedResultsController

I have a model that looks like this:
and want to display it in a UITableView using an NSFetchedResultsController. I want to list all of the Objects for a given Owner, grouped by Group. For example, if we were to view the groups for some Owner Owner A the table view might look like this:
Group A
Object A
Object B
Object C
Group B
Object A
Object D
Group C
Group D
Object C
Object E
It's also important that the Groups be ordered by their name attribute (not shown in the schema above) and that NSFetchedResultsController delegate methods get called whenever Objects are modified or added/removed from a Group.
Given those requirements, I have set up the NSFetchedResultsController to fetch a bunch of Objects with a predicate like [NSPredicate predicateWithFormat:#"ANY groups.owner = %#", someOwner], which throws an NSInvalidArgumentException exception: "to-many key not allowed here". I have tried a few other predicates, but am stuck.
Any suggestions? Is this not something I should be using an NSFetchedResultsController for? Or is there a better way to model my data?
Thanks!
Edit: I actually got the predicate working with the above code, my mistake was in my sectionNameKeyPath argument. I was trying to pass in groups.name, which was what was producing the error. I can see why that way wouldn't work, but am struggling to find a different way to achieve the desired results. Perhaps a join object?
Edit 2: This works somewhat well with a join object like this:
There are two downsides that I see now. The first is that I have to enforce uniqueness myself using code. That's easy enough, but a bit of a nuisance. The second downside, that I don't yet see a way around, is that the NSFetchedResultsController will not call its delegate for updates to Objects anymore. I can live with that downside for now, but am happy to hear better suggestions.
I think the problem you're going to see is that if you have a fetched results controller searching for Objects then it's going to find each Object only exactly once. So each object will appear exactly once in your table. Whereas what you sort of want to do is invert things and find all the Groups, then displaying all the relevant contained Objects. In terms of the fetched results controller, rather than finding rows and thereby being able to divide into sections you want to find sections and thereby figure out what to supply as rows.
The easiest thing, I think, would be to create a fetched results controller on Group and to add an intermediary object of your own that remaps those to sections and supplies group.objects (with a suitably deterministic sorting applied) as the rows per section.
If you want the rows to be dynamic then I guess the easiest thing is to create a fetched results controller per section based on the feedback of the Group controller.
If that's all getting a bit painful and you want to just write your own collection logic then you'll probably want to catch NSManagedObjectContextDidSaveNotification and rerun your logic whenever that occurs. For memory efficiency reasons you probably want to keep hold only of the objectIDs and to get the appropriate objects only when the table view requests them via the existingObjectWithID:error: method on the context. Core Data has a built-in caching mechanism that responds appropriately to memory warnings so that's all written for you.
You shouldn't need a join object to get at data in Many to Many relationships
See this answer: How to deal with many to many relationships with NSFetchedResultsController?
And example project: https://github.com/dmathewwws/Many-to-Many-CoreData

Obj-C, UITableView UI improvement to handle a lot records, aka tap next for more rows?

I'm looking for some technical guidance, perhaps a tutorial.
I'm having to cater for a lot more rows / cells in a table view that I had anticipated.
I already have the view / screen developed / released, however its slow if theres a lot of records, which hasn't been the case until this upcoming release.
I think I'll need to use limit / offset with my sqlite query, however I have sections in my table etc.
I'm also going to have to create a cell / row where users tap to say, show the next 50 records.
This must be quite a common issue, which must have been discussed before.
My issue is the time it will take to develop this feature. I'm too far into the project to convert to core data
Maybe Core Data could be a valid solution. If you use a NSfetchedResultController (with its NSFetchRequest) linked with a UITableView, items are managed for you.
But if you don't want to use Core Data, here a simple suggestion on how to do that:
Load in the model a bunch of data (say the first 50 items). Once loaded, the model is used to present data on the table. When the user reach the end of the table, I show a "Show more" label in the footer section for that table view. Then if the user click on it, load again data (other 50 items), cache them in the model and reload the table, and so on.
This trick can be applied if you have one section and multiple rows.
Hope it helps.
EDIT
If you don't want to use Core Data I'll do the following (some guidelines).
Using LIMIT SQL statement to limit the number of results (e.g. 20).
In your .h create an offset variable like:
NSUInteger offset;
and a method like:
- (NSArray*)fecthMore;
Then in .m, implement that method like the following:
- (NSArray*)fecthMore
{
// using the offset to retrieve the array of results (e.g resultArray)
// the first time you grab the first 20 elements (0-19), the second time the next 20 (20-39)
// and so on
offeset += [resultArray count]; // next 20
return resultArray;
}
With the returning array update eac time the model.
Ray Wenderlich has a great tutorial on using NSFetchedResultsController:
http://www.raywenderlich.com/999/core-data-tutorial-how-to-use-nsfetchedresultscontroller
This will definitely solve the issues you're having with performance on large tables. Using the NSFetchedResultsController is quite easy. The more difficult part will be moving your app from sqlite to Core Data, but fortunately his previous tutorials might be helpful as well.

Using NSFetchedResultController with no sorting

1) I wanna display my search results in the same order returned by the web Service, but it seems 'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'.
2) I still wanna use a NSFetchedResultsController because I allow user to sort by date, etc, but if no sorting is chosen I want to display them in the exact order I got them.
3) Another thing, depending on the search, the items might have different priority. Since I store every item, I cannot just create a priority for each since it won't apply to every case.
Thanks in advance
Lucas,
If you want to enforce an order, then you need an attribute to sort against. I suggest you add a serial number to your model and bump it as you insert items.
Andrew

Find SUM of an array of managed objects

I have a big problem with taking a simple Total Cost from an array!
I have an entity called currentCost, it contains two attributes: costAmount and costDesc. I bind it to an array controller and I have no problem when I add and remove items to and from this entity. The problem is to get the Total Cost from the costAmount attributes and show it in a text field.
I think I have to fetch all costAmounts to another array and take the total of them, but it seems I don't know how to do this! What is the best approach ?
Thank you in advance.
Bind the text field to the array controller and specify #sum.costAmount as the key path.

WCF data services - Limiting related objects returned based on critera

I have an object graph consisting of a base employee object, and a set of related message objects.
I am able to return the employee objects based on search criteria on the employee properties (eg team) etc. However, if I expand on the messages, I get the full collection of messages back. I would like to be able to either take the top n messages (i.e. restrict to 10 most recent) or ideally use a date range on the message objects to limit how many are brought back.
So far I have not been able to figure out a way of doing this:
I get an error if I attempt to filter on properties on the message (&$filter=employee/message/StartDate gives an error ">No property 'StartDate' exists in type 'System.Data.Objects.DataClasses.EntityCollection`1).
Attempting to use Top on the message related object doesn't work either.
I have also tried using a WebGet extension that takes a string list of employee IDs. That works until the list gets too long, and then fails due to the URL getting too long (it might be possible to setup a paging mechanism on this approach)...
Unfortunately the UI control I am using requires the data to be in a fairly specific hierarchical shape, so I can't easily come at this from starting on the message side and working backwards.
Outside of making multiple calls does anyone know of a method to accomplish this with wcf data services?
Thanks!
M.
Looks like the only real way of doing this is in fact to reverse the direction of the query.
So instead of starting from the Employee, I go from the message side. You can filter back on the employee properties, and restrict on the Messages collection. Its not ideal, as it means iterating the collection on return to re-center it on the employee for what I am attempting to do, but it will work. The async nature of silverlight and rich client at least means while an extra iteration is required, it still appears to be reasonably fast.
Another interesting thing to note: the current version of odata/wcf data services does not support querying on properties of inherited classes, so I had to move the start/end date properties up to the base class in order to be able to restrict my search on them.
http://Site/Service.svc/Messages()?&$filter=Employee/OfficeName eq 'Toronto' and (year(StartDate) eq 2010 and month(StartDate) ge 9 )