NSURLRequest : Post data and read the posted page
Asked Answered
Y

3

18

In iOS (current target 5.0, Base SDK 5.1) how can I send a post request to a server, and then read the contents of the page. For example, the page takes a username and password, and then echos true or false. This is just for a better understanding of NSURLRequest.

Thanks in Advance!

Yardley answered 24/4, 2012 at 14:46 Comment(2)
this is a frequent question, did you try to google it?Dierdre
@Dierdre It's not a problem to post a question here if SO doesn't already have the question. I've googled and google sent me here.Striking
L
62

A few things first

  • Decide how you want to encode your data - JSON or url-encoding are a good start.
  • Decide upon a return value - will it be 1, TRUE or 0, FALSE, or even YES/non-nil nothing/nil.
  • Read up on the URL Loading System, it's your friend.

Aim to make all your url connections asynchronous so your UI remains responsive. You can do this with NSURLConnectionDelegate callbacks. NSURLConnection has a small drawback: your code must be decoupled. Any variables you want available in the delegate functions will need to be saved to ivars or in your request's userInfo dict.

Alternatively you can use GCD, which, when coupled with the __block qualifiers, allows you to specify error/return code at the point you declare it - useful for one off fetches.

Without further ado, here's a quick and dirty url-encoder:

- (NSData*)encodeDictionary:(NSDictionary*)dictionary {
      NSMutableArray *parts = [[NSMutableArray alloc] init];
      for (NSString *key in dictionary) {
        NSString *encodedValue = [[dictionary objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 
        NSString *part = [NSString stringWithFormat: @"%@=%@", encodedKey, encodedValue];
        [parts addObject:part];
      }
      NSString *encodedDictionary = [parts componentsJoinedByString:@"&"];
      return [encodedDictionary dataUsingEncoding:NSUTF8StringEncoding];
    }

Using a JSON library like JSONKit makes encoding things easier, consider it!

Method 1 - NSURLConnectionDelegate async callbacks:

// .h
@interface ViewController : UIViewController<NSURLConnectionDelegate>
@end

// .m
@interface ViewController () {
  NSMutableData *receivedData_;
}
@end

...

- (IBAction)asyncButtonPushed:(id)sender {
  NSURL *url = [NSURL URLWithString:@"http://localhost/"];
  NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:@"user", @"username", 
                            @"password", @"password", nil];
  NSData *postData = [self encodeDictionary:postDict];

  // Create the request
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  [request setHTTPMethod:@"POST"];
  [request setValue:[NSString stringWithFormat:@"%d", postData.length] forHTTPHeaderField:@"Content-Length"];
  [request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];
  [request setHTTPBody:postData];

  NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request 
                                                                delegate:self];

  [connection start];  
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  [receivedData_ setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
  [receivedData_ appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
  NSLog(@"Succeeded! Received %d bytes of data", [receivedData_ length]);
  NSString *responeString = [[NSString alloc] initWithData:receivedData_
                                                  encoding:NSUTF8StringEncoding];
  // Assume lowercase 
  if ([responeString isEqualToString:@"true"]) {
    // Deal with true
    return;
  }    
  // Deal with an error
}

Method 2 - Grand Central Dispatch async function:

// .m
- (IBAction)dispatchButtonPushed:(id)sender {

  NSURL *url = [NSURL URLWithString:@"http://www.apple.com/"];
  NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:@"user", @"username", 
                            @"password", @"password", nil];
  NSData *postData = [self encodeDictionary:postDict];

  // Create the request
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  [request setHTTPMethod:@"POST"];
  [request setValue:[NSString stringWithFormat:@"%d", postData.length] forHTTPHeaderField:@"Content-Length"];
  [request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];
  [request setHTTPBody:postData];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Peform the request
    NSURLResponse *response;
    NSError *error = nil;
    NSData *receivedData = [NSURLConnection sendSynchronousRequest:request  
                                           returningResponse:&response
                                                       error:&error];    
    if (error) {
      // Deal with your error
      if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
        NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error);
        return;
      }
      NSLog(@"Error %@", error);
      return;
    }

    NSString *responeString = [[NSString alloc] initWithData:receivedData
                                                    encoding:NSUTF8StringEncoding];
    // Assume lowercase 
    if ([responeString isEqualToString:@"true"]) {
      // Deal with true
      return;
    }    
    // Deal with an error

    // When dealing with UI updates, they must be run on the main queue, ie:
    //      dispatch_async(dispatch_get_main_queue(), ^(void){
    //      
    //      });
  }); 
}

Method 3 - Use an NSURLConnection convenience function

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

Hope this helps.

Larine answered 24/4, 2012 at 16:10 Comment(5)
@Matt Which option would be best if i want to make 2 fetches from a web service, one for usersArray and another for userPoints array and use them to populate a detail type uitableviewcell?Output
@Matt, please see this post: #17415691Output
@Matt Why would we dispatch the sendSynchronous NSURLConnection call to the main queue? Arent we ON the main queue and should dispatch it to another queue? Im confused :sOutput
Mistake. Updated the answer to reflect that.Larine
if you get a 406 HTTP error status code, remove " charset=utf-8" from [request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];Pournaras
P
5
  NSData *postData = [someStringToPost dataUsingEncoding:NSUTF8StringEncoding];

  NSURL *url = [NSURL URLWithString:someURLString];
  NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
  [req setHTTPMethod:@"POST"];
  [req setValue:[NSString stringWithFormat:@"%d", postData.length] forHTTPHeaderField:@"Content-Length"];
  [req setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];
  [req setHTTPBody:postData];

  NSError *err = nil;
  NSHTTPURLResponse *res = nil;
  NSData *retData = [NSURLConnection sendSynchronousRequest:req returningResponse:&res error:&err];
  if (err)
  {
    //handle error
  }
  else
  {
    //handle response and returning data
  }
Publias answered 24/4, 2012 at 15:7 Comment(4)
@Lefteris Asynchronous requests are done similarly and Agent 404 should find out how by googling. I thought the question was mainly about post data and that doesn't change in asynchronous requests. However, I think very often it is not necessary to fire asynchronous, so why do recommend to use it always?Publias
Because synchronous block the main thread, a.k.a user interface, and the user might be wondering what's happening...Rizas
@Rizas Actually, saying it blocks the main thread is not completely correct. It only blocks the thread it's running in, so if that thread is a background thread, then it's not blocking the main thread.Astomatous
@Astomatous Correct, but it's the most common scenarioRizas
A
0

This project might be quite handy for your purpose. It will take care of your downloads and store it locally. Check out the link https://github.com/amitgowda/AGInternetHandler

Aoristic answered 2/12, 2013 at 16:13 Comment(2)
This doesn't really answer the question.Shores
Even its not implemented properly. @DonCruickshankLannielanning

© 2022 - 2024 — McMap. All rights reserved.