mvvmcross editting a complex object over multiple pages and saving data - mvvm

I'm fairly new to mvvmcross and the mvvm model in general. I have been trying to create my own cross platform app for several weeks now and I'm stuck at what would be good practice. I have two main problems, I hope somebody can help me with
Question 1:
I have a complex model with many properties, sub items, and sub items in those sub items. Also, many values are automatically calculated based on other values.
I implemented the MvxNavigatingObject everywhere, and all values are correctly notified when changes occured. So far so good.
Now I want to let people use the app to change the values in my model. But because there are so many input fields, I want to divide the data over several pages. But each page has it's own view model of course. That means reloading my large object every time the page changes.
To solve this, I created a DataHolderService, which is loaded as a singleton on all the view models. Then I let my viewmodels change the data in the DataHolderService and I never have to reload the data.
But I wonder, is this good practice? It feels a bit strange to be doing this. Are there other possibilities? Like using the same viewmodel on multiple pages?
Question 2
I would like to save my data to the database so it persists between sessions. I have a SQLite database and am able to save the data using a button. But if the user forgets to use the save button and the app is put in the background until the system eventually kills it, the data would be lost.
I therefore added a timer, which periodically saves the data to the database. But I can understand that this isn't very good practice. What would be a good way to save the data back into the database without having the user needing to press a save button? Is there an event/function that will fire before the view model is disposed?

Its a bit hard to understand exactly what you are trying to achieve, however.
Is it not better to use a list of questions rather than spreading it over multiple pages?
We have created a similar page recently. If your data is shown as a checkbox/radio buttons/spinner etc, we save them immediately when the value has changed.
For saving text, we use a 1 second timer that starts when the user starts typing, is reset if the user changes the text within that time and is saved otherwise.

Related

How to Remove No Longer Used OData Bindings?

Assume a page that shows a complex data structure (for example, an article with many details). This view will be reused from time to time by rebinding it to different articles.
Now, I noticed that the ODataModel keeps all used article entities in memory (also if they are no longer bound to any control).
This will lead to two issues:
Memory consumption increases over time (if application will not be reloaded).
If the application forces a refresh of the data model, all entities will be loaded (also not used).
The second issue seems to be the bigger problem. It slows down the speed of the application.
I have not found a solution for that problem. If I use refresh(true, true) it seems all data will be reloaded.
Is there an way to clean the model?
Edit
Lets say you have a list of thousands of articles. User can click on one of the articles and will navigate to a detailed screen of that article.
The OData model in client side will cache this. To see it, do something like:
var oModel = this.getModel("modelName");
look with the debugger into oModel.oData.
If the user now navigates back and chooses the next article, this will be cached as well.
If user does this 1000 times, all articles are now in the model.
If you trigger a oModel.refresh(true);, all these data (of 1000 articles) will be reloaded not only the one bound to the view.
Now my application is not about showing article information. It's a more complex structure with subitems. Each time user is visiting this page, more data will be cached (and re-fetched in case of a refresh call on the model).
Edit 2
The function updateBindings(bForceUpdate?) seems to help a little bit.
Anyhow, the data accumulation is still there in the ODataModel class.
That means: Each visited data path will stay in memory since the next reload (F5) of the full page. If someone uses such an application over a day, the data accumulates and a refresh call on the model will read all data again, if still bound to a view or not.
Try deleteCreatedEntry(oContext). Even though this is not the supposed use case for this method it might work to delete an entity from the model without triggering a backend request.
You could also try if updateBindings(bForceUpdate?) only triggers an update on actually bound entities.
1) I do not really understand your problem here. What is it exactly that you do? OData always holds the result of your request plus a queue of changes to that request. If you create lots of entries while your application is running, of course the memory consumption will increase. If you want to revert back to the original request you can use resetChanges(). THis way the used memory should decrease again. But you lose all your changes to the model.
2) Maybe you should look into Odata filtering (http://www.odata.org/getting-started/basic-tutorial/) so that you only load the entities you really want. If you only want a part of the entity loaded then you should maybe redesign your entities to avoid a lot of overhead.
It is hard to speculate what your exact problem is.
Well, if you know exactly what are you doing, you can try something like this:
this.getModel("modelname").aBindings = []
Better solution would be go through the aBindings array and remove redundant bindings.

Open UIManagedDocument take too much time

Recently, I'm working with a timetable app in iOS, and i get trouble with Core Data.
My app has a main user interface kind of like the original calendar app created by Apple, and i save all my events data in the Core Data database.
I create a UIManagedDocument in order to fetch data from database by using its NSManagedObjectContext, and everything works just fine.
However, i need to use the NSManagedObjectContext to fetch data several times in several different view controllers during the runtime. And every time i do this, i need to reopen the UIManagedDocument, but open the document take too much time(it may take 2 seconds or even more, i have to display a spinner in view).
So here are my questions:
What's the right way to open a managedDocument?(I mean like open it during the lunch image time?)
Is there a way to only open the managedDocument once and keep it open during runtime?(So i can use its context all the time)
Does data store in the managedDocument i create?(I found that if i delete the document, data was gone)
Thanks.
You will get lots of different opinions on how to do this but basically you should only have to open the document once. This is done by some object that does the work and stores it so it can return it again when asked by a different view controller.
Many people create singleton's for this or put it in the App Delegate. I have implemented a protocol that lets me put it where ever it is convenient for a particular application without my other code having to know anything about the object that returns the information. As long as it responds to the protocol it can be the App Delegate, a singleton class, or any other object type.
See importing AppDelegate
The protocol that I put in the above example just returns information about where the database is. In my actual implementation I have an openDatabase method with a call back to let me know when it is done plus automatic initialization and updating methods.
You can also improve your performance by having the open operation happen off the main thread. This keeps your UI responsive but does not show the data any faster and you have to be carefull about managed object contexts and the threads they are in.
Good luck...

iOS Asynchronous NSURLConnection triggering behaviors on different views than the one that call it

Me and my team are currently rookie developers in Objective-C (less than 3 months in) working on the development of a simple tab based app with network capabilities that contains a navigator controller with a table view and a corresponding detailed view in each tab. The target is iOS 4 sdk.
On the networking side, we have a single class that functions as a Singleton that processes the NSURLConnection for each one of the views in order to retrieve the data we need for each of the table views.
The functionality works fine and we can retrieve the data correctly but only if the user doesn't change views until the petition is over or the button of the same petition (example: Login button) is pressed on again. Otherwise, different mistakes can happen. For example, an error message that should only be displayed on the root view of one of the navigation controllers appears on the detailed view and vice versa.
We suspect that the issue is that we are currently handling only a single delegate on the Singleton for the "active view" and that we should change it to support a behavior based on the native Mail app in which you can change views while the data that was asked for in each one of the views keeps loading and updating correctly separately.
We have looked over stackoverflow and other websites and we haven't found a proper methodology to follow. We were considering using an NSOperationQueue and wrapping the NSURLConnections on an NSOperation, but we are not sure if that's the proper approach.
Does anyone have any suggestions on the proper way to handle multiple asynchronous NSURLConnections to update multiple views, both parent and child, almost simultaneously at the whim of the user's interaction? Ideally, we don't want to block the UI or disable the buttons as we have been recommended.
Thank you for your time!
Edit - forgot to add, one of the project restrictions set by our client is that we can only use the native iOS sdk network framework and not the ASIHTTPRequest framework or similar. At the same time, we also forgot to add that we are not uploading any information, we are only retrieving it from the WS.
One suggestion is to use NSOperations and a NSOperationsQueue. The nice thing about this arrangement is you can quickly cancel any in-process or queued work (if say the user hits the back button.
There is a project on github, NSOperation-WebFetches-MadeEasy that makes this about as painless as it can be. You incorporate one class in your classes - OperationsRunner - which comes with a "how-to-use-me" in OperationsRunner.h, and two skeleton NSOperations classes, one the subclass of another, with the subclass showing how to fetch an image.
I'm sure others will post of other solutions - its almost a problem getting started as there are a huge number of libraries and projects doing this. That said, OperationsRunner is a bit over 100 lines of code, and the operations about the same, so this is really easy to read, understand, use, and modify.
You say that your singleton has a delegate. Delegation is inappropriate when multiple objects are interested in the result. If you wish to continue using a singleton for fetching data, you must switch your pattern to be based on notifications. Your singleton will have responsibility for determining which connection corresponds to which task, and choosing an appropriate notification to be posted.
If you still need help with this, let me know, I'll try to post some sample code.

Approaches to save some NSManagedObjects but not others?

I'm working on a Core Data iPhone app that pulls remote resources from the web into NSManagedObjects and saves them locally.
I want the user to be able to designate which of these objects should be saved. This means that some will be saved, but many should be deleted. However, I might want to save and delete at different times - I'd prefer to save designated objects immediately (in case the app crashes), but still keep around the other objects because they're hanging out in table views and such.
One approach I can think of is to have a different persistent store - one for stuff that will be saved, one for stuff that won't; this way I can save the "should be saved" store at any time. However, I would much prefer to keep objects of the same type in the same domain.
Another approach would be to just save at the very end - negating any ability to recover from a crash. But saving at the end would allow me to parse out any objects that weren't designated "should save".
And that's really what I want - a "shouldSave" method in the NSManagedObject class, or at least a save method that I could fire at select objects. But as far as I can tell, neither of those exist.
So, if anyone has any other suggestions, please let me know! It would be greatly appreciated.
CoreData is not for object serialization, it is an object graph serialization. That is an important distinction. Once you have an NSManagedObject it is associated with a context, and CoreData handles saves at context level since that is the only way it guarantee any sort of object graph consistency. In other words, you can't save individual objects because if they have relationships with other objects you would need to also save those objects and it quickly cascades out to the whole graph.
You seem to be worried about crash recovery. If the app crashed and the user relaunched it would they expect to see just the items they saved, or everything that was on the screen before they crashed? If it is the former you should just delete them at save time and remove them from the users view (with some animation), if it is the later you should commit out everything, and potentially delete the objects you are not interested in at another time.

Good strategies for REST -> XML -> Core Data -> UITableView?

What are good practices for asynchronously pulling large amounts of XML from a RESTful service into a Core Data store, and from this store, populating a UITableView on the fly?
I'm thinking of using libxml2's xmlParseChunk() function to parse chunks of incoming XML and translate a node and its children into the relevant managed objects, as nodes come in.
At the same time that these XML nodes are turned into managed objects, I want to generate UITableView rows, in turn. Say, 50 rows at a time. Is this realistic?
In your experience, what do you do to accomplish this task, to maintain performance and handle, potentially, thousands of rows? Are there different, simpler approaches that work as well or better?
Sure, this is a pretty standard thing. The easiest solution is to do the loading in a background thread on one MOC, and have the UI running on the main thread with its own MOC. Whenever you get a chunk of data you want to have appear (say 50 entries), you have the background MOCsave:.
Assuming you have the foreground MOC rigged to merge changes (via mergeChangesFromContextDidSaveNotification:) then whenever you save the background MOC the foreground MOC will get all of those changes. Assuming you are using NSFetchedResultsController it has delegate methods to cope with changes in its MOC, and if you are using Apple's sample code then you probably already have everything setup correctly.
In general CoreData is going to be faster than anything you roll yourself unless you really know what you are doing and are willing to spend a ton of time tuning for your specific case. The biggest thing you can do is make sure that slow things (like XML processing and synchronous flash I/O caused by save:) are not on the main thread blocking user interaction.
Joe Hewitt (Facebook app developer) has release much of his code as open-source. It is called Three20. There is a class there that is great for fetching internet data and populating it into a table, without the need for the data beforehand. The classes used for this are called TTTableViewController and TTTableViewDataSource.
From here, it would not be much of a stretch to store as CoreData, just subclass the classes as you see fit with the supplied hooks.
If you are worried about too much data, 50 at a time does sound reasonable. These classes have a built in "More" button to help you out.
From the Three20 readme:
Internet-aware table view controllers
TTTableViewController and
TTTableViewDataSource help you to
build tables which load their content
from the Internet. Rather than just
assuming you have all the data ready
to go, like UITableView does by
default, TTTableViewController lets
you communicate when your data is
loading, and when there is an error or
nothing to display. It also helps you
to add a "More" button to load the
next page of data, and optionally
supports reloading the data by shaking
the device.
No one has mentioned RestKit yet? My friends ... seriously, you have to check this out. If you are doing anything with REST on iOS (and now on OS X) and particularly if you're wanting to work with Core Data ... PLEASE have a look at RestKit. I've saved countless hours implementing some pretty complex data synchronization between a server and my Core Data models on iOS. RestKit made it so damned easy, it almost makes you sick.