I have a NSURLConnection that gets data from a JSON web service, and everything works fine. I'm using it to post something to the server and get a success response.
After that call I want to initiate another NSURLConnection to refresh the data, so I'm doing so inside the connectionDidFinishLoading method, however this second connection isn't calling connectionDidFinishLoading when it is done loading.
Can I not initiate a NSURLConnection from inside the connectionDidFinishLoading method?
EDIT: Below is the code. I subclassed NSURLConnection to include a Tag NSString, calling the new class NSURLConnectionHelper. I'm using this to differentiate which connection has called the connectionDidFinishLoading.
- (void)connectionDidFinishLoading:(NSURLConnectionHelper *)connection
{
if([connection.Tag isEqual:#"NewMessage"]){
NSString *jsonString = [[NSString alloc] initWithData:receivedNewMessageData encoding:NSASCIIStringEncoding];
NSDictionary *results = [jsonString JSONValue];
[jsonString release];
[connection release];
if ([[results objectForKey:#"MessageAdded"] isEqual:#"True"]) {
User *newUser = [[User alloc] init];
[newUser retrieveFromUserDefaults];
if([newUser IsLoggedIn]){
Message *message = (Message *)[messages objectAtIndex: 0];
NSString *urlAsString = // url for webservice goes here
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLConnectionHelper *connection1 = [[NSURLConnectionHelper alloc] initWithRequest:request delegate:self];
connection1.Tag = #"GetLatestMessages";
[request release];
if (connection1) {
receivedLatestMessagesData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
}
}
}else if([connection.Tag isEqual:#"GetLatestMessages"]){
//do some other stuff but this code is never reached
}
}
I'm not familiar with NSURLConnectionHelper but it looks like you're never starting the connection.
I ended up having a space in my web service url, once I corrected that it worked.
Related
I am building an iOS app in which I want to allow people to order stuff from stores that are registered in my database. To allow my users to see products, companies, etcetera, I need to download array's with the requested information. This is how I do that:
- (void)getOrderItems {
_dbAction = #"getOrderItems";
NSString *post = [NSString stringWithFormat:#"controller=%#&action=%#&orderNumber=%#", #"BakkerFunctions", #"getOrderItems", self.orderNumber];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.mysite.nl/API/"];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Current-Type"];
[request setHTTPBody:postData];
[request setURL:url];
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if(conn)
{
NSLog(#"Connection Successful");
}
else
{
NSLog(#"Connection could not be made");
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data {
NSArray *orderItemsFromDatabase = [[NSArray alloc] init];
orderItemsFromDatabase = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL][#"itemsFromDatabase"];
NSString *datastring = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%# Array:%#", datastring, orderItemsFromDatabase);
//convert array from database in array of custom objects
NSMutableArray *orderItems = [[NSMutableArray alloc] init];
for (int i = 0; i < orderItemsFromDatabase.count; i++) {
[orderItems addObject:
[[Item alloc] initWithItemName:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemName"]
itemDescription:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemDescription"]
itemCode:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemCode"]
itemBarCode:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemBarCode"]
itemPrice:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemPrice"]
itemQuantity:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemQuantity"]
itemUserRemark:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemUserRemark"]
itemCompany:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemCompany"]
productImageNumber:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"productImageNumber"]
category:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"category"]
itemImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.imagine-app.nl/ProductImages/%#%#", [[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"productImageNumber"], #".jpg"]]]]]];
}
self.orderItems = [NSArray arrayWithArray:orderItems];
}
the getOrderItems method is a method inside the custom class I have for each order. when the user selects an orders, this method gets called to download the products that belong to this order. Now this process works all fine, but the problem is that the data gets receiver when my TableView already set itself up.
I want to add an observer to check if the amount of products in this array changes, and if it does, i want to update the tableview. I have searched for hours on SO an google, but not much is said about this and nothing useful to me.
any help would be much appreciated! thank you in advance
A simple solution would be to use a MBProgressHUD or a similar control which will display a message saying that please wait while the data is being downloaded. Once, the data is downloaded it will trigger a callback meaning that it has downloaded the data and then you can refresh your UITableView to reflect all the data.
Override the orderItems setter and reload the table in there.
- (void)setOrderItems:(NSArray*)items
{
_items = items;
[self.tableView reloadData];
}
I'm not adding an observer anymore, As soon as my custom class finished processing the received data, it calls the orders view controller. That made that the data is loaded before the tableview is set up and the problem is solved.
Hello I am developing one application as currency converter in that, I have URL it will return only one country currency but my module look like if user select one country then I need to display list of currency converter values of more than one country so I need call josn more than one times.
code is as:
responseData = [[NSMutableData data] retain];
ArrData = [NSMutableArray array];
NSString *strURL = [NSString stringWithFormat:#"http://rate-exchange.appspot.com/currency?from=%#&to=%#&q=1",strtablbase,strto];
NSURL *url = [NSURL URLWithString:strURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
results = [responseString JSONValue];
livevalues=[responseString JSONValue];
With above code I am geting one country values but I need pass one strto values differently
Is it possible?
If yes, please give suggestions & help me out from this problem.
Yes you can use NSOperation Queues to call the different URL or if you are using Asihttprequest this Link may be useful for you :)
responseData = [[NSMutableData data] retain];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"country1"];
[array addObject:#"country2"];
for (NSString *urlString in array) {
strtablbase = [NSString stringWithFormat:#"%#",urlString];
NSString *strURL = [NSString stringWithFormat:#"http://rate-exchange.appspot.com/currency?from=%#&to=%#&q=1",strtablbase,strto];
NSURL *url = [NSURL URLWithString:strURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
Try this..
Of course it is possible to pass different values. Like you already did you can start connections one after another. If the server belongs to you, I would implement a request which returns me all the rates at a time. It saves the time for sending and receiving request. It really does not matter (for waiting time) if you get 100 bytes or 500 bytes in one request.
Otherwise you need to call many requests. Like said you can call one after another and even 2-3 requests at the same time. You can implement the mechanism your self or you can use NSOperationQueue which is made exactly for many requests by Apple.
For more information https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
And I want yo point the method (of NSURLConnection)
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
I have a EMR app and i want that i may send the data which i have collected like images and voice to server. in data base so how can i do this . Is there any way to send these data to server through post method.
Here is an example of a HTTP Post request
// define your form fields here:
NSString *content = #"field1=42&field2=Hello";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.example.com/form.php"]];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[content dataUsingEncoding:NSISOLatin1StringEncoding]];
// generates an autoreleased NSURLConnection
[NSURLConnection connectionWithRequest:request delegate:self];
Might want to reference http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSURLConnection_Class/Reference/Reference.html
This tutorial is also helpful http://www.raywenderlich.com/2965/how-to-write-an-ios-app-that-uses-a-web-service
In that case, you can do follow two ways:
1. if you strictly like to using POST (i like), u can using cocoahttpserver project:
https://github.com/robbiehanson/CocoaHTTPServer
In iphone app, you can do this code to send POST request:
-(NSDictionary *) getJSONAnswerForFunctionVersionTwo:(NSString *)function
withJSONRequest:(NSMutableDictionary *)request;
{
[self updateUIwithMessage:#"server download is started" withObjectID:nil withLatestMessage:NO error:NO];
NSDictionary *finalResultAlloc = [[NSMutableDictionary alloc] init];
#autoreleasepool {
NSError *error = nil;
NSString *jsonStringForReturn = [request JSONStringWithOptions:JKSerializeOptionNone serializeUnsupportedClassesUsingBlock:nil error:&error];
if (error) NSLog(#"CLIENT CONTROLLER: json decoding error:%# in function:%#",[error localizedDescription],function);
NSData *bodyData = [jsonStringForReturn dataUsingEncoding:NSUTF8StringEncoding];
NSData *dataForBody = [[[NSData alloc] initWithData:bodyData] autorelease];
//NSLog(#"CLIENT CONTROLLER: string lenght is:%# bytes",[NSNumber numberWithUnsignedInteger:[dataForBody length]]);
NSString *functionString = [NSString stringWithFormat:#"/%#",function];
NSURL *urlForRequest = [NSURL URLWithString:functionString relativeToURL:mainServer];
NSMutableURLRequest *requestToServer = [NSMutableURLRequest requestWithURL:urlForRequest];
[requestToServer setHTTPMethod:#"POST"];
[requestToServer setHTTPBody:dataForBody];
[requestToServer setTimeoutInterval:600];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[urlForRequest host]];
NSData *receivedResult = [NSURLConnection sendSynchronousRequest:requestToServer returningResponse:nil error:&error];
if (error) {
NSLog(#"CLIENT CONTROLLER: getJSON answer error download:%#",[error localizedDescription]);
[self updateUIwithMessage:[error localizedDescription] withObjectID:nil withLatestMessage:YES error:NO];
[finalResultAlloc release];
return nil;
}
NSString *answer = [[NSString alloc] initWithData:receivedResult encoding:NSUTF8StringEncoding];
JSONDecoder *jkitDecoder = [JSONDecoder decoder];
NSDictionary *finalResult = [jkitDecoder objectWithUTF8String:(const unsigned char *)[answer UTF8String] length:[answer length] error:&error];
[finalResultAlloc setValuesForKeysWithDictionary:finalResult];
[answer release];
[self updateUIwithMessage:#"server download is finished" withObjectID:nil withLatestMessage:NO error:NO];
if (error) NSLog(#"CLIENT CONTROLLER: getJSON answer failed to decode answer with error:%#",[error localizedDescription]);
}
NSDictionary *finalResultToReturn = [NSDictionary dictionaryWithDictionary:finalResultAlloc];
[finalResultAlloc release];
return finalResultToReturn;
}
Don't forget to pack attributes with images to base64.
Finally, if u don't like to keep data, which u send in you mac app, u can send to u database using any database C api. I recommend to using core data to save receive data.
I am calling NSURLConnection asynchronous method calls in my view controller. I would like to handle TWO RESPONSES FOR TWO REQUEST in the same Delegate. Please suggest me what would the best approach to achieve this? I'm developing in iOS 5 SDK.
UPDATED:
// Class A
[serverconns setDelegate:self];
connection = [serverconns executeAsyncHttpPost :firstjsonrequest];
[serverconns setDelegate:self];
connection = [serverconns executeAsyncHttpPost :secondjsonrequest];
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.appendData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// logs the error
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
// doing something
}
}
//Class B: ServerConnection
- (NSURLConnection *) executeAsyncHttpPost :(id) jsonParams
{
NSString *urlstr = [NSString stringWithFormat:#"%#", baseURL];
urlstr = [urlstr stringByAppendingFormat:method];
NSURL *pUrl = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:pUrl];
NSData *requestData = [NSData dataWithBytes:[jsonParams UTF8String] length:[jsonParams length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody: requestData];
return [[NSURLConnection alloc] initWithRequest:request delegate:delegateResponder startImmediately:YES];
}
-(void) setDelegate:(id)newDelegate
{
delegateResponder = newDelegate;
}
save your connections somewhere (maybe ivar of your delegate)
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
if (connection == yourFirstConnection) {
// doing something for first connection
} else {
// doing something for second connection
}
}
}
just point out some minor problem of your code
NSString *urlstr = [NSString stringWithFormat:#"%#", baseURL];
urlstr = [urlstr stringByAppendingFormat:method];
should replace to
NSString *urlstr = [baseURL absoluteString];
urlstr = [urlstr stringByAppendingString:method];
and add two(or more or array) weak/assign property of NSURLConnection to your class A (connection delegate)
#property (assign) NSURLConnection *myFirstConnection;
#property (assign) NSURLConnection *mySecondConnection;
// assume only need to handle two connection otherwise NSArray should be used instead
than in your class B (create connection)
- (NSURLConnection *) executeAsyncHttpPost :(id) jsonParams
{
NSString *urlstr = [baseURL absoluteString];
urlstr = [urlstr stringByAppendingString:method];
NSURL *pUrl = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:pUrl];
NSData *requestData = [NSData dataWithBytes:[jsonParams UTF8String] length:[jsonParams length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody: requestData];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegateResponder startImmediately:YES];
delegateResponder.myFirstConnection = connection;
// delegateResponder.mSecondConnection = connection;
return connection;
}
If I were you I would create a CustomClass which inherits the NSURLConnection. And I will add property called tag.
When I initiate the CustomClass, I would set the tag property and use that to determine which request is being worked on
CustomURLConnection *connection = [[CustomURLConnection alloc] initWithRequest:request delegate:self tag:1];
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate tag:(int)_tag
{
if(self = [super initWithRequest:request delegate:delegate])
{
self.tag = _tag;
}
Now in the code you posted add this
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
if (connection.tag == 1){
}
}
}
return self;
}
I think all the mentioned solutions are "ugly". I would not implement a solution with delegate methods but instead create a blocks-based solution. I could post an example if you're interested. I would make use of the AFNetworking classes for this approach.
What follows is an example of a class that handles 2 different responses without using a delegate implementation, opting for blocks instead with the AFNetworking library.
- (void)JSONFromService
{
// create the first request and set the methods that handle the return values (either NSData or NSError in this case) in blocks ...
NSURL *firstURL = [NSURL URLWithString:#"http://dl.dropbox.com/u/6487838/test1.html"];
NSURLRequest *firstRequest = [NSURLRequest requestWithURL:firstURL];
AFHTTPRequestOperation *firstOperation = [[AFHTTPRequestOperation alloc] initWithRequest:firstRequest];
[firstOperation setCompletionBlockWithSuccess:^ (AFHTTPRequestOperation *operation, id object)
{
NSString *firstString = [[NSString alloc] initWithData:object encoding:NSASCIIStringEncoding];
NSLog(#"%#", firstString);
} failure:^ (AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[firstOperation start];
// create the second request and set the methods that handle the return values (either NSData or NSError in this case) in blocks ...
NSURL *secondURL = [NSURL URLWithString:#"http://dl.dropbox.com/u/6487838/test2.html"];
NSURLRequest *secondRequest = [NSURLRequest requestWithURL:secondURL];
AFHTTPRequestOperation *secondOperation = [[AFHTTPRequestOperation alloc] initWithRequest:secondRequest];
[secondOperation setCompletionBlockWithSuccess:^ (AFHTTPRequestOperation *operation, id object) {
NSString *secondString = [[NSString alloc] initWithData:object encoding:NSASCIIStringEncoding];
NSLog(#"%#", secondString);
} failure:^ (AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[secondOperation start];
}
I usually subclass NSURLConnection and add properties to store whatever context I need to handle the response.
Since the delegate methods get NSURLConnection passed in, you can just cast it back to your subclass and access the context.
Take a look at this example.
I think you should keep all of your connections in an activeConnections array. Every time one finishes, you do [activeConnections indexForObject:connection] and you update your delegate method accordingly, using the index.
Now, a cleaner way to do it( and a better way from my point of view, but this depends on how large is the data you want to transfer) is to use queues. I'll provide a small example and add comments to it:
// we assume you have 2 requests: req1, req2
//now, create a new dispatch queue
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netqueue",DISPATCH_QUEUE_SERIAL);
//execute the operations in the queue ASYNC
//is very important to dispatch this ASYNC on a background thread, otherwise your UI will be stuck until the request finishes
dispatch_async(netQueue,
^{
// We are on a background thread, so we won't block UI events (or, generally, the main run loop)
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *data = nil;
//We can call the request synchronous and block this thread until completed
data = [NSURLConnection sendSynchronousRequest:req1
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
//call your delegate with the appropriate method for req1
//be sure to copy the contents in data, as we will reuse it with the next request
});
//We can call the other request synchronous and block this thread until completed
data = [NSURLConnection sendSynchronousRequest:req2
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
//call your delegate with the appropriate method for req2
});
//and this can go on forever. If you have many requests to execute, simply put them in a loop
});
dispatch_release(netQueue);
I am making one application in which one webservices need to be called, is it possible to call webservices related to JSON in background,when user press homebutton in iPhone
You can call asynchronous request using
NSMutableURLRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"Your webservice URL here"]];
[[NSURLConnection alloc]initWithRequest:request delegate:self];
where you can implement delegate methods
In your Button Method
//yourURL is the webservice URL
[self performSelectorInBackground:#selector(loadDataFromWebservice:) withObject:yourURL];
-(void) loadDataFromWebservice : (NSString *)strUrl
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url=[[NSURL alloc] initWithString:strUrl];
NSData *data = [NSData dataWithContentsOfURL:imgUrl];
//Do your manipulations with data
//If you want to update any UI with the webservice data
[self performSelectorOnMainThread:#selector(assignDataViews:) withObject:responseObject waitUntilDone:YES];
[pool release];
}
//Return any object which you are comfortable with. I returned NSArray
-(void)assignDataViews : (NSArray *) yourObject
{
//Do all your UI changes here
}