FetchedResultsController and UITableView in multiple views - iphone

Summary: I have two different views which use tables to show results from a fetched results controller. These tables may contain the same data. I get errors when moving the rows in one table after the other table has been loaded. (Obvious I suppose!)
To simplify, imagine an entity consisting of countries, and another entity grouping these countries together into sets of countries. We have an "editSet" view which allows you to name the set, add or delete countries in the set, as well as reorder them, all using the standard UI. We then have a "viewSet" view that shows you these countries and some value associated with them (eg. exchange rate or whatever).
Now, in the edit view, when we reorder and moveRowAtIndexPath is invoked, I set a BOOL which stops any further UI changes until the coredata is updated (each record has a displayOrder integer which I update before doing another performSearch). This all works perfectly if you only have instantiated the "editSet" view.
Where things goes wrong is if you load "viewSet" then load "editSet" with the same set and move the rows. The BOOL we set in editSet doesn't get passed along to viewSet (which is also "watching" the coredata) and gets upset when the coredata is changed programmatically. This generates:
Serious application error. An exception was caught from the delegate of
NSFetchedResultsController during a call to -controllerDidChangeContent:
. *** -[NSMutableArray removeObjectAtIndex:]:
index 0 beyond bounds for empty array with userInfo (null)
And all hell breaks lose.
On the other hand, if I load/appear "viewSet" with A DIFFERENT SET to the one I am editing, no problems.
So, what I need to do is EITHER "disconnect" the FRC and the table when leaving viewSet (perhaps do a search on nothing and reload the table?) OR pass a BOOL to the viewSetController when saving the moved rows in coredata in editSet to mimc what I do locally in that viewcontroller (not quite sure how to do this but doable I guess).
I am sure I am not the first to come across this problem, so wondering, what's the best way?

Use another managed object context for your "edits" and then merge them back in when you go back to your "viewSet". Take a look in the Apple sample code project 'CoreDataBooks' to see how you can use two contexts to perform disjoint edits.

Related

Delayed Table Name Resolution in View

I have a view over a table. It turns out the table gets moved and an updated version of it created each night. This ensures there is always a table of the expected name present in the database, but I cannot find a way to make my view continue to point to the current version of the table. Whichever table existed when the view was created is the one I end up pointing to even after it moves and goes stale.
ViewA:
select a, b, c from todays_table;
todays_table stays current all day, then at night it gets renamed to todays_table01. View A now points to todays_table01 and a new table shows up called todays_table. Again, todays_table is current, but ViewA no longer is.
Is there a way to delay the table name resolution until the view is used? I haven't been able to get EXECUTE IMMEDIATE working for SELECT statement. I think I could get a dynamic SQL statement working if I used a cursor, but I have never needed these before and I'm not sure if they are the right path. I read about AUTO_REVAL but I believe this would only delay resolution until the first time the view was used and still go stale that night.
I could, of course, stop using the view and just move the complex query into my program but there are many places it is needed so I would like to eliminate all other solutions before falling back to this.
It would be ideal to eliminate the temporary table and just have the master table receive updates throughout the day but this is beyond my comprehension as I know nothing about RPG II and OCL.
Thanks for reading.
Edit
Per #Mr. Llama's suggestion, I experimented with using synonyms and aliases to point to todays_table and then having my view point to the synonym. Unfortunately in this scenario, the view uses the alias to resolve the actual table name on creation so the view continues to point to todays_table when it is renamed to todays_table01, though the alias continues to reference todays_table.
Edit 2
I'm accepting #mustaccio's answer because it does work and would be a reasonable approach to this problem if I could get the parameters going where they need to. My particular project requires flexibility so I am actually going to jump on the nightly process bandwagon and add a program to recreate my views after the process messes with their references as #danny117 suggested.
Thanks to everyone who replied though, I learned a lot about how all of these pieces work together.
I think you might be able to achieve what you want by wrapping your view definition in a SQL table function, something like
CREATE FUNCTION insteadofview (<parameters>)
RETURNS TABLE (<columns>)
...
RETURN
SELECT <the rest of your view definition>
Depending on how you query your view, you will probably need to pass search criteria into the function as parameters, otherwise performance will be suboptimal because the function will have to return all rows from the query before search arguments can be applied.
According to the manual, as you have noticed views on a table that is renamed continue to point to the original table object. Routines, however, including table functions, will be invalidated and their plans prepared again when they next invoked, using the original source table name.
I have no way of testing this though.
Full syntax to create a table function.

Pass complex object through controllers (core data)

I am completely lost.
The problem is passing data through viewcontrollers in my wizard.
My project contains 4 viewcontrollers:
Step1ViewController, possibility to fill in name
Step2ViewController, possibility to fill in nickname
Step3ViewController, possibility to fill in emailaddress,
Step4ViewController, possibility to fill in interests, finish
All viewcontrollers are pushed to a navigation controller. It's possible to go to the next step by clicking the bar button on the navigationtoolbar. What I want to achieve is to collect all the data in the steps and create save a NSManagedobject in the last step (by clicking the finish button). So when an user quits in step 2 and he restarts the app, there should be no saved object. So he will restart the wizard. When there is a personobject in core data then another view is loaded (this is a condition in the delegate class)
I know when you have a simple model schema passing data can be easily done to the controllers by using the prepareForSegue method. Collect all variables and create and save a core data object. For passing data back to the previous step I can use protocols.
But in my application my model schema is way more complex. My wizard contains about 18 steps and there are a lot of assiociated models for the Person model (like trainingsplan, interests, etc.) so I think collecting all data in variables and combine them all in the last step is really not a good approach.
What is the best way to do this?
I uploaded a wizard sample application with a couple of steps and 2 models (Person and interests (one-2-many)). Hopefully this will make it more clear. Feel free to modify the code: https://github.com/stalkert/WizardPrototype
Two ideas come to mind - each has their own pros and cons. Both use the concept of a mutable dictionary that will hold all the various data that you need at the end. In addition, you will add a key='step' with an NSNumber object.
1) Assuming that all the view controllers do not exist initially, he first controller creates the dictionary, adds the data it should supply to it, and sets the 'step' to two. It then creates and runs a new viewController, passing the dictionary off to it in a property. The second verifies the step is correct, adds what it should, then passes it to another controller. The 'step' here acts as a test that in fact the dictionary is at the stage it should be.
2) Assuming the view controllers are already instantiated then use the same 'step' concept as above, but use notifications. When the first step is complete, either store the dictionary in defaults, a class or singleton object, or the appDelegate (in a property). Send out a notification - and add a userInfo that is either the full dictionary or a number providing the next step. The controller that should do step 3 can see that its turn is up so it should become active (switch a UITabBarController tab automatically etc).
In either case, when the last step is complete, send the dictionary to the class managing the repository or do it directly.

An event for "this row was just loaded" in GWT cell framework

I have a list of ids of rows that should be selected, but not the actual objects that will be selected. For example, I know Users 16 and 25 should be selected, but I don't have an instance representing them. This could be because they're on a different page of data that I haven't loaded yet.
I want to be able to select these users programmatically even though their data is not loaded yet. I'm implementing a function called setSelectedIds() and it's working great - I scan all visible objects, and if their id matches one of the ids in my set, I set it Selected. Likewise, if the user changes a selection through the human interface, I catch the SelectionChangeEvent and determine whether an id should be added or removed to my backing list of ids.
The actual question:
Is there an event that's always fired when data has been loaded via updateRowData()? The only thing missing from my implementation is a way to handle the loading of new data. I need to be notified when new data is loaded, so I can decide whether to select it or not. RangeChangeEvents happen to soon - those handlers are fired before the data is loaded, and selectionModel.getSelected() returns some null objects. RowCountChangeEvents only happen when the total number of rows changes. What am I missing?
Can't you implement your own SelectionModel? When it's asked whether an object isSelected, it compares its ID with your list of selected IDs. You could even generalize it by using the object's key (given by the ProvidesKey) rather than an hard-coded getId.

Updating/Inserting existing objects in NSFetchedResultsController

I'm using NSFetchedResultsController with a UITableView which displays a list of folders and associated unread counts for each item in that folder. I'd like to insert/delete folders w/ animation from the tableView based on the unread count during a sync/refresh (on a background thread utilizing NSManagedObjectContextDidSaveNotification). Folders with no unread items should fade out, and existing folders with new unread items should fade in.
Currently using the controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: method does not do this unless the tableView is completely reloaded, or the app is relaunched, which is not ideal.
Is NSFetchedResultsController not the way to do this? My initial idea was to create a separate Core Data entity that only holds the folders with unread items, and add/remove folders from that, but that just seems hokey.
My NSPredicate looks something like:
ANY items.unread == 1
Update:
The above NSPredicate works fine and grabs the objects that I expect it to. My problem is that if the app launches and FolderX has 0 unread items, it does not appear. If I then refresh (goes out and parses a JSON file in a background thread) and FolderX now has 25 unread items, it will not automatically fade in or trigger NSFetchedResultsChangeInsert and since it wasn't included in the first place, it also doesn't trigger NSFetchedResultsChangeUpdate which is why I thought the solution was to create a separate Entity that only holds Folders with unread items and add/remove from that during sync.
I just feel like I'm missing something painfully obvious.
Update 2:
I've simplified the problem as much as I can by removing the influence of my own app, I'm able to replicate it in the default Core Data template in Xcode.
The New Problem:
There is a single Entity named "Event" with no relationships and two properties, "Date" and "unreadCount". I have NSFetchedResultsController set up with an NSPredicate that looks like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"unreadCount < 10"];
Every time a new object is added, it is created with an unreadCount of 15, then all existing objects are decremented by 1. This is done on a separate ManagedObjectContext (to replicate background processing, but is all done on the main thread).
I've implemented the controllerWillChangeContent and related methods, and what I expected to happen was that after an existing object (with unreadCount of 15) is decremented 5 times (to now be 10) it should be inserted into the tableView because it now matches the predicate. This is not at all what happens, the object does not appear at all unless the app is restarted.
I've attached a sample project, simply run it and click the Add button a few times (nothing will happen). Restart the app and you will see a handful of objects now in the tableView, if you click the Add button a few more times those objects will remove themselves, but new objects will not get added.
If NSFetchedResultsController is not the solution to get this behavior to work, then what is?
Sample Project: http://dl.dropbox.com/u/521075/BGUpdating.zip
Your predicate will not work crossing two or more to-many relationships. You have at present:
type == folder AND ANY (set of folders).(set of items).unread == 1
You can't use ANY to transverse an arbitrary number of relationships. Instead, you probably need a subquery like:
type == folder AND (0!=SUBQUERY(folder,$f,0!=SUBQUERY($f.items,$i,$i.unread==1).#count).#count)
I think you maybe performing the fetch on the wrong entity. If you are presenting a table of Folder objects, you should be performing the fetch on the Folder entity. That alone would simplify everything. Your predicate would then be just:
ANY items.unread==1

If we have a view over another view and we drop the parent view, what happens to the other view?

If we have a view over another view and we drop the parent view, what happens to the other view? After we recreate the base view, will the second view be active again?
From my understanding of views in DB2 (and SQL in general), they act effectively as aliases to SQL select statements. I would expect that in this situation, your child view would still exist, but querying it when the parent was deleted would result in an error.
Since the definition of the child view is stored and remains static, recreating the parent view with the same object names should result in the child view returning the expected result again.
This would be very easy for you to verify yourself, by the way. :-)
Probably the same thing that happens in this question.
Michael Sharek's response is correct; the remaining views that depended on the view that was dropped will remain invalid (VALID='N' for that row in SYSCAT.VIEWS) even after the dropped view is replaced. You will need to reissue the create statements for any view in SYSCAT.VIEWS where VALID='N', but the good news is that you can overwrite an invalid view without dropping it.
What I typically do is use EXPORT to extract a copy of the TEXT column of every view in SYSCAT.VIEWS where VALID = 'N'. Then I execute the DDL statements in that file and the invalid views are all typically replaced on the first pass. However, if you have a more sophisticated hierarchy of interdependent views, you may need to run the file a couple more times. There's no need to filter out the DDL for the views that were made valid during a previous pass; those statements will be safely rejected with a duplicate object error.