NSURLRequest with multiple parameters - iphone

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

Related

How to pass info with NSURLConnection instance so I can get it from connectionDidFinishLoading

I am using NSURLConnection to load data from a response. It works as it should, the delegate method connectionDidFinishLoading has the connection instance with the data I need. The problem is that I want to pass some information along with the request so that I can get it when the connection finishes loading:
User wants to share the content of a URL via (Facebook, Twitter,
C, D).
NSURLConnection is used to get the content of the URL
Once I have the content, I use the SL framework
SLComposeViewController:composeViewControllerForServiceType and need
to give it the service type
At this point I don't know what service the user selected in step 1. I'd like to send that with the NSURLConnection.
Can I extend NSURLConnection with a property for this? That seems very heavy-handed. There must be a "right way" to do this.
Many Thanks
Assuming you don't need the delegate-based version of the NSURLConnection process for some other reason, this is a good use case for the block-based version:
- (void)shareContentAtURL:(NSURL *)shareURL viaService:(NSString *)service
{
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:shareURL];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] == 0 && error == nil) {
// handle empty response
} else if (error != nil) {
// handle error
} else {
// back to the main thread for UI stuff
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// do whatever you do to get something you want to post from the url content
NSString *postText = [self postTextFromData:data];
// present the compose view
SLComposeViewController *vc = [SLComposeViewController composeViewControllerForServiceType:service];
[vc setInitialText:postText];
[self presentViewController:vc animated:YES];
}];
}
}];
}
Since blocks can capture variables from their surrounding scope, you can just use whatever context you already had for the user's choice of service inside the NSURLConnection's completion block.
If you're still wed to the delegate-based NSURLConnection API for whatever reason, you can always use an ivar or some other piece of state attached to whatever object is handling this process: set self.serviceType or some such when the user chooses a service, then refer back to it once you get your content from the NSURLConnectionDelegate methods and are ready to show a compose view.
You could check the URL property of an NSURLConnection instance and determine the service by parsing the baseURL or absoluteString property of the URL with something like - (ServiceType)serviceTypeForURL:(NSURL *)theURL;
All the NSURLConnectionDelegate methods pass the calling NSURLConnection object-so you could get it from
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
or
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

Multiple URL calls in same class

I am new to iphone development could somebody help me out this problem ,From one week i am facing with one issue , that is i have multiple urls like below
for (int i=0;i<=[listingAffArray];i++)
NSString *urlStr=[NSString stringWithFormat:#"http://demo.holidayjuggle.net:7777/services/inventoryservice/%#/%#/stores/search?location=12.971598700000000000,77.594562699999980000,50",appDelegate.buyingAff,[appDelegate.listingAffArray objectAtIndex:i]];
}
in this i am getting response from all the urls but in didfinishloading could not able to find which urls responsedata
NSURL *url=[NSURL URLWithString:urlStr];
NSMutableURLRequest *req=[NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"GET"];
[req setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[req setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
connection=[NSURLConnection connectionWithRequest:req delegate:self];
if(connection){
NSLog(#"connection is successfull ");
}
else{
NSLog(#"connection failed");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
responseData=[[NSMutableData alloc]init];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *strResponse=[[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
}
in responsedata only last url data is there i put breakpoint and observed each url is calling in didfinishloading , when the second url is calling it is upadating with the secondurl call like that in responsedata last url data only is there .How to store each response data seperately
Thanks in advance
sivakumari
Create an array in your class and store each strResponse in your array (using addObject).
Also, this doesn't make sense:
for (int i=0;i<=[listingAffArray];i++)
NSString *urlStr=[NSString stringWithFormat:#"http://demo.holidayjuggle.net:7777/services/inventoryservice/%#/%#/stores/search?location=12.971598700000000000,77.594562699999980000,50",appDelegate.buyingAff,[appDelegate.listingAffArray objectAtIndex:i]];
}
The [listingAffArray] part should give you a compiler error and, even if that did work, you would be overwriting the same variable each time through the loop.
Yes it happen because all NSURLConnection share same delegate same object which is "self"
If u want to load multiple URL than you should have multiple space to store that data.
So one object of responseData is not sufficient.
There can be many way let me suggest one which I use.
Declare a NSMutableDictionary object
Store NSURLConnection object as key and NSMutableData object as value so if u have 3 URL you have 3 entery in NSMutableDictionary.
In every Delegate method of NSURLConnection append data only to corresponding NSMutableData object.
Tell me if u need more help....
Try to call web service with url in a asynchronous manner.
ie.Hit the first url and when you received the result of first url the hit the second url request. after that when u received the second url response hit the third url request.
These all request should run in background thread or a new thread, so that it doesn't effect the main thread.
Also take a enum data type like
enum {
requestOne=0,
requestTwo,
requestThree,
requestFour
}currentRequest;
when u start hitting the first url in currentRequestData assign requestOne and in response check wit this enumDataType. when response received then hit second url with seconod enum type

how call nsurlconnection delegate methods continuosly in iphone

hi in one of my application. i have to send a request to the server (json server) many times continously.my url will be like this as mention below
#"http://185.185.116.51/servername/serverjspfilesname.jsp?filterID=21&ticket=65675656565656567"
actually i have many filter id's (filter id you can find at top).in order to chnage the filterid continously i used for loop like this as mention below
for(int i=0;i<[appdelegate.listOfFiltersArray count];i++)
{
filtersDataModelObject=[[ListOfFiltersDataModel alloc]init];
filtersDataModelObject=[appdelegate.listOfFiltersArray objectAtIndex:i];
homescreenstring=[NSString stringWithFormat:#"http://%#/servername/serverjspfilesname.jsp?filterID=%#&ticket=%#",Ip,filtersDataModelObject.filterID,[storeData stringForKey:#"securityTicket"]];
NSLog(#"url is %#",homescreenstring);
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:homescreenstring]];
connection=[[NSURLConnection alloc]initWithRequest:request delegate:self];
if(connection)
{
homeScreenResponseData=[[NSMutableData alloc]init];
}
else
{
NSLog(#"connection failed");
}
}
actually after each condition is satisfied in for loop i have to connect with the server for getting the data from the server using nsurlconnection delegate methods. but here after complete execution of for loop only nsurlconnection delegate methods are executing with last filterid which is getting from the appdelegate.listOfFiltersArray array.
but i would like to call the server for each filterid.
if anyone know please let me know.thanks in advance.
Create one count variable int count in .h file.
int count = 0 //in .m file
Now use this method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//one request finished
count ++;
//request another using count
}
The solution that Prince proposed is not general as you will face the problem of defining the
"filterid" for each request.
And what you are doing is a bad approach. Dont mix the web request with your business logic code.The web stuff should be handled by a separate file handling all the requests throughout the app. And that class will implement delegation.
For delegation you need to do the following.
In your Network class header (Networkclass.h) add the protocol
#protocol NetworkControllerDelegate <NSObject>
-(void) UrlResponseRecieved :(NSData *)responseData;
-(void) UrlResponseFailed:(NSError *)error;
#end
and in NetworkClass.m (implementation fie) do the following
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (receivedData != nil)
{
if([delegate respondsToSelector:#selector(UrlResponseRecieved:)])
[self.delegate UrlResponseRecieved:receivedData];
[receivedData release];
receivedData = nil;
}
[connection release];
}
if you still get stuck you may refer to the following
What's wrong on following URLConnection?
or you may read any asynchronous downloading tutorial first.

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.

multipart/x-mixed-replace with iPhone SDK

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];
}