multipart/x-mixed-replace with iPhone SDK - iphone

I'm trying to download several images in response to a single http request. On the server side (java) I'm using oreilly multipart response and I'm getting my datas in my iPhone Simulator in didReceiveData (approximately one call for each image) after a call to didReceiveResponse (approximately one call for each image as well) in my delegate.
The problem is this approximately... Has anyone ever managed to handle correctly multipart/x-mixed-re with iPhone SDK ? If yes what is the best strategy here ? Should I play with the expected length ? on server side ? on client side ? should I wait until I've received everything... mmmh that doesn't even seen enough as the calls to didReceiveData happens in a random order (I'm asking picture1,picture2 and I'm sometimes receiving picture2,picture1 even though the order is respected on server side !). Should i temporize between pictures on server side ?
Or should I drop multipart/x-mixed-replace ? what would be the easiest then ?
That's a lot of questions but I'm really stuck here ! Thanks for you help !

I'm not sure what your final use for the images is, but the intended purpose of the multipart/x-midex-replace content type is for each received part to completely replace the previously received responses. Think of it like frames of a video; only one picture is displayed at a time and the previous ones are discarded.
Temporizing is almost never a foolproof solution. Especially on the iPhone you're going to encounter an unimaginable variety of network situations and relying on a magic number delay between frames will probably still fail some of the time.
Since you have control of the server, I'd recommend dropping the multipart. Make sure when you are sending multiple requests to the server that you don't block the main thread of your iPhone app. Use NSOperations or an alternative HTTP library (like ASIHTTPRequest) to make your image fetch operations asynchronous.

I did that successfully using this code. The important thing is to create 2 buffers to receive your data. If you use only one you will have some double access problems (stream access and jpg CODEC access) and corrupted JPG data.
Do not hesitate to ask me for more details.
- (IBAction)startDowload:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://210.236.173.198/axis-cgi/mjpg/video.cgi?resolution=320x240&fps=5"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"GET"];
/*
I create 2 NSMutableData buffers. This points is very important.
I swap them each frame.
*/
receivedData = [[NSMutableData data] retain];
receivedData2 = [[NSMutableData data] retain];
currentData = receivedData;
urlCon = [[NSURLConnection alloc] initWithRequest:req delegate:self];
noImg = YES;
frame = 0;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
// it can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is declared as a method instance elsewhere
UIImage *_i;
#try
{
_i = [UIImage imageWithData:currentData];
}
#catch (NSException * e)
{
NSLog(#"%#",[e description]);
}
#finally
{
}
CGSize _s = [_i size];
[imgView setImage:_i];
[imgView setNeedsDisplay];
[[self view] setNeedsDisplay];
}
/*
Buffers swap
*/
if (currentData == receivedData)
{
currentData = receivedData2;
}
else
{
currentData = receivedData;
}
[currendData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the currentData (NSData buffer)
[currendData appendData:data];
}

Related

NSURLRequest with multiple parameters

I am trying to set up a NSURLRequest to download a simple index.html with its externa style.css sheet but I am not quite sure how to do this.. I have only ever just formatted the URL of the request to the file I want.. but this has to be slightly different and I cannot find a good example of what I am trying to do.
this is my code so far:
#pragma mark - NSURLConnection methods
- (void)htmlRequest
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.mywebsite.com/index.html"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
NSLog(#"Connection Fail");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// inform the developer of error type
}
// This method uses methodName to determin which Initalizer method to send the response data to in EngineResponses.m
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// EngineResponses *engineResponses = [EngineResponses sharedManager];
// [engineResponses GetManufacturers:receivedData];
NSString *myString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#", myString);
}
as you can see I am just calling index.html directly.. I would like to know how to format my request so i get the index.html as well as style.css
any help would be greatly appreciated.
I always create a new data structure,which has a -connection property and a -request property,like this
#interface connectionWrapper : NSObject
#property(retain) NSURLRequest *request
#property(retain) NSURLConnection *connection
by retaining this data structure in an mutable array, you can distinguish the connections in callback methods by iterate the array and compare each connectionWrapper instance's -connection property with the connection parameter the of the callback method, if they match(points to a same object), then you can retrieve the -request property of the connectionWrapper instance, then -url property of NSURLRequest instance.
as I'm not an native English speaker, I think code is a better tutor.
-(NSURLRequest*)getRequestByConnection:(NSURLConnection*)connection
{
for(connectionWrapper *w in theArrayContainingAllConnectionWrappers)
{
if(w == connection)
return w.request;
}
}
In callback method:
-(void)connection:(NSURLConnection*)connection didReceiveResponse(NSURLResponse*)response
{
NSURLRequest *request = [self getRequestByConnection:connection];
NSURL *url = [request url];
/*apply different approach to different url/*
}
PS:it's very sad that NSURLConnection don't have a -request property so that we can retrieve the request associated with the connection easily.
One way or another, you will have to make 2 requests. Even if you open a web page directly in a web browser, the browser will make a separate request for the CSS file referenced in the HTML it downloads. If your application needs both the HTML and the CSS file, then you want it to make 2 separate URL requests, first to get the HTML and then to get the CSS file.
Now, just because 2 requests need to be made, that doesn't mean you will always need to write the code that makes those 2 requests. It may be that libraries like the ones recommended by #Slee automatically take the results of a first request, parse them out, and make requests for any referenced CSS files. I have not worked with them so I am not sure what they support, or if any libraries will do this for you.
One thing you may want to consider is loading the HTML and CSS through a UIWebView rather than handling it all manually. UIWebView will attempt to load, parse, and render an HTML file into a UI component. In the process it will load referenced CSS and JavaScript files and apply them to its rendering. If you want to do anything special like intercept the calls it makes to load the CSS file(s), you can implement the UIWebViewDelegate protocol and set the delegate of the the UIWebView. Within that delegate you can implement the -webView:shouldStartLoadWithRequest:navigationType: method to be notified when the web view is loading the CSS file. You can use the call to that method to look at the request that is being issued for the CSS and do something else interesting with the request.
do you know the name of the .css file?
If so I would just make 2 requests otherwise you will have to write a parser to look for the link to the css and make a second request anyways.
I'd also suggest looking into a library to handle the downlading of stuff - lot's of great libraries that can do the heavy lifting for you with advanced features.
Here's 3 I have used:
http://blog.mugunthkumar.com/coding/ios-tutorial-advanced-networking-with-mknetworkkit/
https://github.com/tonymillion/TMHTTPRequest
https://github.com/pokeb/asi-http-request

nsurlconnection asynchronous request

First of all the questions are failry simiple.. if you just want to see what they are skip to the bottom of this post and you will see them in bold.. for more detail then you can read the rest of this post...
I am just trying to iron out my NSURLConnection so that its working smoothly and I understand this properly. There is a profound lack of example/tutorials for Asynchronous connections on the internet or not any that I can find that explaine what is going on with any level of depth other than getting the connection up and running which after working on it seems pretty simple. Hopefully this question can full the void that I feel is out there for other users.
So, in my .h file i have imported the foundations headers and declared the methods required for the received or lack of received data (errors etc).
.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h> //add foundations
//.. other headers can be imported here
#interface MyViewController: UITableViewController {
//Im not setting any delegates to access the methods because Is all happening in the same
//place so I just use the key word 'self' when accessing the methods declared below
//I'm not sure if this is the best thing to do but I wasn't able to get my head around declaring the delegate or how it would help me with the way I have set up my request etc.
}
- (IBAction)setRequestString:(NSString *)string; //this method sets the request and connection methods
//these methods receive the response from my async nsurlconnection
- (void)receivedData:(NSData *)data;
- (void)emptyReply;
- (void)timedOut;
- (void)downloadError:(NSError *)error;
So thats my header file.. pretty simple not much explaining needed.
.m
//call setRequestString from some other method attached to a button click or something
[self setRequestString:#"rss.xml"];
//..
- (IBAction)setRequestString:(NSString *)string
{
//Set database address
NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:#"http:www.comicbookresources/feeds/"]; // address not real jsut example
//append the string coming in to the end of the databaseURL
[databaseURL appendString:string];
//prepare NSURL with newly created string
NSURL *url = [NSURL URLWithString:databaseURL];
//AsynchronousRequest to grab the data
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil){
[self receivedData:data];
}else if ([data length] == 0 && error == nil){
[self emptyReply];
}else if (error != nil && error.code == NSURLErrorTimedOut){ //used this NSURLErrorTimedOut from foundation error responses
[self timedOut];
}else if (error != nil){
[self downloadError:error];
}
}];
}
now set up the methods that were initialized in the .h file and called in the if statement above
- (void)receivedData:(NSData *)data
{
NSString* newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", newStr); //logs recived data
//now you can see what data is coming in on your log
//do what you want with your data here (i.e. start parsing methods
}
- (void)emptyReply
{
//not sure what to do here yet?
}
- (void)timedOut
{
//also not sure what to do here yet?
}
- (void)downloadError:(NSError *)error
{
NSLog(#"%#", error);
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error!" message:#"A connection failure occurred." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[errorAlert show];
}
Cool so that pretty much the basics of what I have done right there.. now the questions I have are as follows.
Question one:
Where I call NSURLConnection like so
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
What is happening here what is the ^ for is that executing that whole block (including the if statements) on a different thread or something? because it looks alot like grand central dispatch formatting but slightly different.
Question two:
what should I be doing inside emptyReply & timedOut methods?
Question three:
How would I incorporate caching into this? I would like to cache the responses I get back from different requests. i.e. with my setRequestString you will see there is a string input parameter, so i can request different rss feeds with the same method.. I need to figure out how to cache these responses into individual caches.. but im not sure where to start with it.
Finally
If you have made it this far, thank you very much for reading my question. Hopefully with your responses we can get a pretty nice solution going here.. that other people can use for themselves and pick and choose the bits and peices they need that works for there own solution..
Anyway thank you very much for reading and I look forward to your replies.. even if they are just refrences to tutorials or examples you think might help me.. anything is good I just want to fully understand whats going on and whats a good solution.
Read about blocks in Apple documentation. Its new. Or you can read here
You can show errors such as request timed out etc. You don't really have to handle them separately than the error one unless you have special logic.
Try this for caching
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:TIMEOUT_INTERVAL];

Call webservice repeatingly, objective c

I have a webservice that returning 20 results each time (it is a limitation of the service provider). I want to call this service 10-20 times repeatingly and update my UI each time.
Is there best practice for this situation? I do not want to block the ui while calling the server. This causes problems if the user want to perform actions while the action in progress
(like navigating away from the current page)
Thanks!!!
what you can do is call the webservice in a background thread, collect the required data and jump back to main thread and update the UI.
We are doing the above(i.e jumping from background thread to main thread) because it is not recommended to update any UI in the background process.
you can call you webService in background by using
[self performSelectorInBackground:#selector(MyWebService) withObject:nil];//you can pass any object if you have
and to come back on main thread when the background task is over you can do..
[self performSelectorOnMainThread:#selector(myMainFunction) withObject:nil waitUntilDone:YES];
you can change the last parameter i.e. waitUntilDone:No also. By doing this, user will not have to wait till the UI is updated. they can carry there task.
you can use NSTimer for periodic calling your webService.
hope that helped :)
It depends on how you want to display the information.
If you're using the asynchronous connection (in my opinion, more effective than calling a synchronous connection in the background) and its delegate, it should not block the user interface:
- (void)loadData {
NSString *urlString = #"http://www.stackoverflow.com";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
// delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// clear out or intialize instance data variable
[myData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// convert data to whatever it's supposed to be (for example, array)
NSString *dataString = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
NSArray *dataArray = [parser parseStringToArray:dataString];
[myArray addObjectsFromArray:dataArray];
//update tableview either using reload data (instant) or using updates (for smooth animation)
}
You can then recall the loadData method at the end of didFinishLoading: method to loop it.

Opening a streaming connection to an HTTP server on iPhone/Cocoa environment?

I've been using NSURLConnection to do a HTTP post to establish the connection. I've also implemented the didReceiveData delegate to process incoming bytes as they become available.
As incoming data comes in via didReceiveData, I add the NSData to a data buffer and try parsing the bytesteam if enough data has come in to complete a message segment. I'm having a hard time managing the data buffer (NSMutableData object) to remove bytes that have been parsed to structs. Was curious if there's an easier way. My didReceiveData delegate is below.
It works, but I don't think I'm managing memory correctly after I copy the message segment (currMsg) out of the responseData buffer and call processMsg. I get double free errors when running under the Simulator -- the program doesn't crash.
NSMutableData/NSData provide methods for appending bytes to the end but I didn't see any methods for removing bytes from the beginning (bytes representing whats already been parsed. I would appreciate some advice on how to best remove the parsed bytes from the responseData buffer. I come from a mostly C background so I'm not sure if there are better ways of manipulating the NSData bytes pointer. I'd like to avoid copying if possible -- just want to process a portion of the responseData buffer and leave the rest in responseData for next time enough bytes are in it for parsing.
Thanks
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSData *tmpBuffer = nil;
NSInteger currMsgSize = 10;
[responseData appendData:data];
NSInteger rspDataLen = [responseData length];
while(rspDataLen >= 10) {
currMsg = [[NSData alloc] initWithBytesNoCopy:(void *)[responseData bytes] length:currMsgSize];
[self processMsg:currMsg];
[currMsg release];
[responseData getBytes:tmpBuffer range:NSMakeRange(currMsgSize, rspDataLen - currMsgSize)];
[responseData release];
responseData = [[NSMutableData alloc] initWithBytesNoCopy:(void *)tmpBuffer length:rspDataLen - currMsgSize];
rspDataLen = rspDataLen - currMsgSize;
}
}
Where do you allocate the first responseData?
What is [self processMsg:currMsg] doing with the data? If it is expecting the data to be around after -processMsg: returns, and it isn't explicitly making a copy, then you are in trouble.
Infact, unless you have finished with the received data before didReceiveData: returns, you need to make a copy of it somewhere, which isn't visible in the code shown.
You need to allocate the storage for tempBuffer, not pass in an uninitialised pointer;
You should look probably for a pre-rolled implementation of a simple ring buffer. There are plenty around.

NSURLConnection - how to wait for completion

Our iPhone app code currently uses NSURLConnection sendSynchronousRequest and that works fine except we need more visibility into the connection progress and caching so we're moving to an async NSURLConnection.
What's the simplest way to wait for the async code to complete? Wrap it in a NSOperation/NSOperationQueue, performSelector..., or what?
Thanks.
I'm answering this in case anyone else bumps into the issue in the future. Apple's URLCache sample code is a fine example of how this is done. You can find it at:
iOS Developer Library - URLCache
As John points out in the comment above - don't block/wait - notify.
To use NSURLConnection asynchronously you supply a delegate when you init it in initWithRequest:delegate:. The delegate should implement the NSURLConnection delegate methods. NSURLConnection processing takes place on another thread but the delegate methods are called on the thread that started the asynchronous load operation for the associated NSURLConnection object.
Apart from notifications mentioned prior, a common approach is to have the class that needs to know about the URL load finishing set itself as a delegate of the class that's handling the URL callbacks. Then when the URL load is finished the delegate is called and told the load has completed.
Indeed, if you blocked the thread the connection would never go anywhere since it works on the same thread (yes, even if you are using the asynch methods).
I ran into this because our app used NSURLConnection sendSynchronousRequest in quite a few places where it made sense, like having some processing occurring on a background thread occasionally needing extra data to complete the processing. Something like this:
// do some processing
NSData * data = someCachedData;
if (data = nil) {
data = [NSURLConnection sendSynchronousRequest....]
someCachedData = data;
}
// Use data for further processing
If you have something like 3 different places in the same flow that do that, breaking it up into separate functions might not be desirable(or simply not doable if you have a large enough code base).
At some point, we needed to have a delegate for our connections(to do SSL certificate pinning) and I went trolling the internet for solutions and everything was of the form: "just use async and don't fight the framework!". Well, sendSynchronousRequest exists for a reason, this is how to reproduce it with an underlying async connection:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse *__autoreleasing *)response error:(NSError *__autoreleasing *)error
{
static NSOperationQueue * requestsQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
requestsQueue = [[NSOperationQueue alloc] init];
requestsQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;
});
NSCondition * waitLock = [NSCondition new];
[waitLock lock];
__block NSError * returnedError;
__block NSURLResponse * returnedResponse;
__block NSData * returnedData;
__block BOOL done = NO;
[NSURLConnection sendAsynchronousRequest:request
queue:requestsQueue
completionHandler:^(NSURLResponse * response, NSData * data, NSError * connectionError){
returnedError = connectionError;
returnedResponse = response;
returnedData = data;
[waitLock lock];
done = YES;
[waitLock signal];
[waitLock unlock];
}];
if (!done) {
[waitLock wait];
}
[waitLock unlock];
*response = returnedResponse;
*error = returnedError;
return returnedData;
}
Posted here in case anyone comes looking as I did.
Note that NSURLConnection sendAsynchrounousRequest can be replaced by whatever way you use to send an async request, like creating an NSURLConnection object with a delegate or something.