What would be the most elegant way to receive data from a streaming JSON API using AFNetworking? AFNetworking provides excellent support for receiving non-streaming data from JSON APIs, but I couldn't find any examples of streaming JSON.
AFNetworking does not have a built-in streaming SAX-style JSON operation, but it should be straight forward to create your own.
Create a subclass of AFJSONRequestOperation
Use the outputStream property of the operation during initialization to hook it up to a JSON parser that supports SAX-style parsing (such as Yajl). The parser will read data and build up the JSON object as it comes in
Hook up the responseJSON property to read the cached object from the parser
...or depending on how you're doing streaming, you may need to add a new property that defines a block to execute when new JSON objects come back (e.g. setReceivedJSONBlock:((^)(void (id JSON))block). This block will probably be triggered by delegate methods sent from the parser (e.g. <YAJLParserDelegate> -parserDidEndDictionary:).
If you are able to get this working, I would encourage you to publish and share this with others. I think this could be useful to quite a few people.
Related
I have several layers of ActionFunctions for api service, e.g. authorization and rate limiting. After implementing a file upload I realized that I'd really like authorization and rate limiting to happen before the file is uploaded, not after.
(I built a custom body parser for a file uploading, but let's say I have a giant json - why to parse it it's thrown away?)
Does anyone have an idea how to do it?
You can't do it with the ActionFunctions, as you said they use parsers to parse the body before the checks are done.
You can have checks before the parsing of the body by using EssentialAction. But you'll need to re-implement the logic of the compositions
Does anyone know if it is possible to write to the response stream in OpenRasta rather than returning an object as a response resource? Alternatively, am I able to implement an HTTP handler but still leverage OpenRasta's URL rewriting?
Thanks
Chris
You can always keep an http handler on the side to do specialized things, but that ties you to asp.net and will prevent your code from being portable on other hosts. If that's something you're ok with, any handler that's registered for a specific route will get executed before openrasta on asp.net.
that said, codecs are the ones writing to the response stream, so provided you have a custom IMediaTypeWriter you can write the resource instance on a stream whichever way you want.
Say for example that you returned an IEnumerable from your handler, as those get deferred executed, you can just start the enumeration of those in your custom codec without any problem.
I have a page that displays one string on a blank page. I need to retrieve that string. How would I do it if I wanted to do it both, synchronously and asynchronously? (I would appreciate if someone gave me the method names for both synch and asynch).
Thank you,
You can use NSString's stringWithContentsOfURL:encoding:error: method to get the HTML page synchronously and later parse it using NSXMLParser.
Since stringWithContentsOfURL:encoding:error: is a blocking call, you should put it in a method and invoke the method using performSelectorInBackground:withObject:. This will retrieve the HTML page in the background.
Once you have the string, create an instance of NSXMLParser and get the string. This one is asynchronous process.
Few examples on how to use NSXMLParser - Make NSXMLParser your friend and BNR - Parsing XML in Cocoa.
Have a look at the NSURLConnection reference. The methods you want are there. Also, read the URL Loading guide, it has some code examples for both synchronous and asynchronous loading.
The body data of the repsonse will be the string you want. You'll need to be a bit careful about which encoding was used by the web site. If not specified in the response at all, it will be ISO-8859-1, otherwise it will probably be specified in the Content-Type header of the response. You then need to convert the data to an NSString with initWithData:encoding:
NB if you are not on the main thread, you can use NSStering's stringWithContentsOfURL:usedEncoding:error:. You shouldn't use this on the main thread because it is a synchronous method.
I am just testing an app to get data off our web server, previously I had been using:
NSURL, NSURLRequest, NSURLConnection etc. to get the data that I wanted.
But I have just noticed that if I swap to using XML I can simply do the following and pass the results to NSXMLParser:
NSURL *url = [NSURL URLWithString:#"https://www.fuzzygoat.com/turbine?nbytes=1&fmt=xml"];
Am I right in thinking that if your just after XML this is an acceptable method? It just seems strongly short compared to what I was doing before?
gary
That code only creates a URL object that represents a URL. It doesn't make any request or download any data. You still need to use NSURLRequest and NSURLConnection in order to actually download any data from the server.
Also, stay away from methods like 'initWithContentsOfURL:` and friends, unless you understand that they will block the thread that they are called on until complete. For network services, this method shouldn't be used because it'll block your UI for an indeterminate time, because you can't predict how fast the internet connection will be wherever the app is used.
NSURLConnection's asynchronous request system is exactly what you need. It won't block the UI, and provides a nice encapsulated interface to downloading data from a remote location.
This is definitely the right way to go. There do exist many different connection methods (including my favorite, ASIHTTPRequest) and many, many different xml parsers (including my favorite, KissXML) that are faster or more memory efficient than the Apple built in methods.
But to answer your question, yes, your logic and design pattern is correct.
UPDATE: Because Jasarien seems to think the question talks about asynchronous actions, I will discuss that here. ASIHTTPRequest handles async very very easily. Just check out the quick samples.
Depending on how much XML you get back from the web service, NSXMLParser may not be ideal because the entire XML document has to be read into memory.
Memory is pretty scarce on an iPhone, so using a SAX parser like that in libxml2 is probably better for larger XML files. Instead of reading the entire document into memory, the XML is streamed through and parsed for specific nodes of interest. The memory overhead is lower because less data is stored at once.
Once a node of interest is parsed, an event handler is called to do something useful, like store the node data somewhere.
In this case, take a look at Apple's XMLPerformance sample project for example code.
I have a question and I am very open to suggestions (even very odd ones!)
I am writing an iPhone app, which does a request (URL with parameters) to a server. As a response, the iPhone receives XML. All is well.
Right now, I am looking to improve my application's speed by measuring the time it takes to perform certain tasks. I've found that, of all the tasks performed (downloading, XML Parsing, sending the request, handeling the request, parsing objects from XML), downloading the actual XML takes the longest.
Now, my XML files are very, very, very easy and very, very, very small. I use them primarily to read RSS-like data and show them in a UITableView.
My app works very well, and there is nothing that feels really slow, but there is one application in the App Store right now which does something very similar to my application, but is way faster and feels more 'snappy', if you know what I mean. It also has the great feature to load the headlines one by one from the RSS-feed.
Currently I'm experimenting with gzip compression of my data, but the compression only makes my data half the size and it doesn't seem to do any real good for performance. The main thing is, that the data has to be downloaded, before it gets parsed. It would be very cool to have a 'stream' of data, which is parsed as it comes in. That way, I can do two jobs almost simultaneous and load headlines one by one (making user interactivity more attractive).
Anyone has an idea of how to improve my performance? Either by great compression tips or entirely different ways to communicate with the server.. All is welcome!
UPDATE: putting the latency and responsiveness of the server aside; how could I get a source of XML to be 'streamed' to my iPhone (downloaded byte for byte) and at the same time get parsed? Right now it is a linear process of downloading -> parsing -> showing, but it could become semi-parallel by downloading & parsing at the same time (and show each item when it is done downloading, instead of loading it all at the same time in a UITableView)
Assuming you're using NSXMLParser's initWithContentsOfURL:, that's probably part of the problem. It seems like that downloads the entire contents of the URL, then hands it off to the parser to parse all at once.
Despite the fact that the NSXMLParser is an event-driven parser, it doesn't seem to support streaming data to the parser in an incremental manner. You could, of course, replace NSXMLParser with some other parsing library that handles incremental data in a more sensible way.
An alternative would be to use NSURLConnection, and create a new NSXMLParser and re-parse the data each time some data comes in, in the connection:didReceiveData: method of your NSURLConnection's delegate. You''d have to write some extra code to ignore the extra events from re-parsing the beginning of the file more than once.
This seems like it'd be more work than just grabbing some other library and adapting it, but maybe not, depending on how you're handling the downstream creation of your table data.
If NSMutableArray is the underlying data structure of your UITableView you should try
using initWithContentsOfURL. The format on the server needs to be in apples "plist" xml which is easy to generate. I'm guessing that if cocoa already has resources acquired for
processing xml it would be quicker to use them instead of creating your own xml parser instance.
How are you parsing the XML data. The need to parse XML as you receive it is the whole reason pull parsing was invented, and they have the NSXMLParser event driven parser that operates on a stream of data...
That also means compression of the data is counterproductive.
For very small amounts of XML, the issue of speed is probably about latency of connection.
Thus, other big factor could be the DNS lookup. You could do that yourself once beforehand and cache the IP, perhaps rechecking only on failure to connect to your server...
Since your xml files are very small, (just 8k or so, I imagine?) I don't think you'd see a big performance boost trying to parse them as they come in. You can use an asynchronous NSURLConnection to do that, but I can't see it helping with a small file very much. Have you considered the time it takes to generate the XML file on the server? Are you using PHP to create the XML, or accessing a static file? For testing purposes, it might be interesting to access static files and see how that compares.
I ran into a problem like this a while ago, and it turned out the mySQL database on my server was being slow, and it really had nothing to do with my app.
Also, use Instruments to look at the amount of memory that is allocated while you're downloading and parsing an XML document. Some XML parsers load the entire XML document into memory and then allow you to index randomly into the document's elements, while others provide step by step parsing only. The step-by-step method is much better for limited devices like the iPhone and it's probably what you're using, but a quick look at memory consumption should tell you.