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.
Related
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...
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.
I am pulling data from a web service in order to populate my UITableView rows. It loads perfectly fine, however it takes around 4 seconds in order to load the whole data. Is there a way in that I can increase the time to load? Probably by caching it? Or any tips and tricks on what people usually do to do this?
You can display old data and asynchronously fetch using threads the new data and then reload the UITableView
Caching depends on what you're loading. If you're loading, say, Twitter feeds, you should cache the user avatar pictures because you know you'll be fetching them over and over again. If you're writing something like a retail app, you might show items that are on sale. If the items change every Sunday, then cache them the first time you fetch them and don't fetch them again until Sunday. That sort of thing.
Beyond that, there's not much you can do to make the internet faster. If you have control over the web service, you can make the data sent back as concise and simple as possible. You'd be surprised how many milliseconds you can burn parsing complicated XML.
If it makes sense for your app, you can show old data. For a twitter client, it's better to just save the data you've already fetched, show it immediately, and load the new stuff in the background.
If you can't do any of that, then pretty much all you can do is put up a "Loading..." overlay of some sort so that the app doesn't just look frozen and live with the delay.
You can try to use Three20 TTTableViewController, nice tutorial can be found here:
Three20
Moreover, you can add "Load more results" button, look here
I am new to objective-c/cocoa programming. I am making an application which is to constantly sync with a server and keep its view updated.
Now in a nutshell, heres what I thought of: Initiate an NSTimer to trigger every second or two, contact the server, if there is a change, update the view. Is this a good way of doing it?
I have read elsewhere that you can have a thread running in the background which monitors the changes and updates the view. I never worked with threads before and I know they can be quite troublesome and you need a good amount of experience with memory management to get most out of them.
I have one month to get this application done. What do you guys recommend? Just use an NSTimer and do it the way I though of...or learn multithreading and get it done that way (but keep in mind my time frame).
Thanks!
I think using separate thread in this case would be too much. You need to use threads when there is some task that runs for considerable amount of time and can freeze your app for some time.
In your case do this:
Create timer and call some method (say update) every N seconds.
in update send asynchronous request to server and check for any changes.
download data using NSURLConnection delegate and parse. Note: if there is probability that you can receive a huge amount of data from server and its processing can take much time (for example parsing of 2Mb of XML data) then you do need to perform that is a separate thread.
update all listeners (appropriate view controllers for example) with processed data.
continue polling using timer.
Think about requirements. The most relevant questions, IMO, are :
does your application have to get new data while running in background?
does your application need to be responsive, that is, not sluggish when it's fetching new data?
I guess the answer to the first question is probably no. If you are updating a view depending on the data, it's only required to fetch the data when the view is visible. You cannot guarantee always fetching data in background anyway, because iOS can always just kill your application. Anyway, in your application's perspective, multithreading is not relevant to this question. Because either you are updating only in foreground or also in background, your application need no more than one thread.
Multithreading is relevant rather to the second question. If your application has to remain responsive while fetching data, then you will have to run your fetching code on a detached thread. What's more important here is, the update on the user interface (like views) must happen on the main thread again.
Learning multithreading in general is something indeed, but iOS SDK provides a lot of help. Learning how to use operation queue (I guess that's the easiest to learn, but not necessarily the easiest to use) wouldn't take many days. In a month period, you can definitely finish the job.
Again, however, think clearly why you would need multithreading.
I'm working on a Core Data-based application that has a Mac application acting as a 'server' and an iPhone as a client. Everything is going swimmingly, except I'm running into performance issues.
When the user taps an object, the server must return some objects related to that object (nothing too heavy, usually 3-4 objects) and show a UI to choose some options. This needs to be as fast as possible. The round-trip time to the server, the server pulling the data, formatting it, returning it to the client, and the client creating NSManagedObjects from the data (which cannot be optimized further) is about 200 ms. The code relating to presenting the UI (which cannot be optimized further, again) requires around 150 ms. On an iPod touch 2G running iOS 4.0, the single line of code saving the managed object context after the objects are imported is taking anywhere from 150-200 ms.
To me, this screams that I should be backgrounding the managed object context saving. However, as far as I understand it, that won't really meet my needs. If I want to save the managed object context on a background thread, then all the objects in it must have been created on a background thread in a separate managed object context, so I won't see any speed gain because it will still take 100-200 ms for the save to occur, and I'll be seeing even more overhead because I'll still need to tell my main thread to update it's managed objects from the backgrounded managed object context's save before my view controller sees that it needs to refresh itself.
Am I missing an obvious solution? Is there something about Core Data I could use in this situation that would help? I hate to throw such a general question like this out there, but I'm at a complete loss where to go from here.
Sounds like you need to move the entire server communication to a background thread. If you did that then the entire UI would be responsive no matter how long the communication with the server took.
To do this, you stand up a second NSManagedObjectContext on the background thread connected to the same NSPersistentStoreCoordinator. Then you perform your server communication on that background thread (it might even make sense to use an NSOperation) and save the changes.
Your main thread and therefore main NSManagedObjectContext listens for save notifications and when it receives one it updates the main thread and UI. This will eliminate any freezing you are seeing and the processing time becomes mostly irrelevant.