Append Data to a Post Nsurlrequest

Append data to a POST NSURLRequest

If you don't wish to use 3rd party classes then the following is how you set the post body...

NSURL *aUrl = [NSURL URLWithString:@"http://www.apple.com/"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];

[request setHTTPMethod:@"POST"];
NSString *postString = @"company=Locassa&quality=AWESOME!";
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

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

Simply append your key/value pair to the post string

NSURLRequest : Post data and read the posted page

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.

How to make POST NSURLRequest with 2 parameters?

It will probably be easier to do if you use AFNetworking. If you have some desire to do it yourself, you can use NSURLSession, but you have to write more code.

  1. If you use AFNetworking, it takes care of all of this gory details of serializing the request, differentiating between success and errors, etc.:

    NSDictionary *params = @{@"firstname": @"John", @"lastname": @"Doe"};

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager POST:urlString parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"responseObject = %@", responseObject);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"error = %@", error);
    }];

    This assumes that the response from the server is JSON. If not (e.g. if plain text or HTML), you might precede the POST with:

    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
  2. If doing it yourself with NSURLSession, you might construct the request like so:

    NSDictionary *params = @{@"firstname": @"John", @"lastname": @"Doe"};

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[self httpBodyForParameters:params]];

    You now can initiate the request with NSURLSession. For example, you might do:

    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (error) {
    NSLog(@"dataTaskWithRequest error: %@", error);
    }

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
    NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
    if (statusCode != 200) {
    NSLog(@"Expected responseCode == 200; received %ld", (long)statusCode);
    }
    }

    // If response was JSON (hopefully you designed web service that returns JSON!),
    // you might parse it like so:
    //
    // NSError *parseError;
    // id responseObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
    // if (!responseObject) {
    // NSLog(@"JSON parse error: %@", parseError);
    // } else {
    // NSLog(@"responseObject = %@", responseObject);
    // }

    // if response was text/html, you might convert it to a string like so:
    //
    // NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    // NSLog(@"responseString = %@", responseString);
    }];
    [task resume];

    Where

    /** Build the body of a `application/x-www-form-urlencoded` request from a dictionary of keys and string values

    @param parameters The dictionary of parameters.
    @return The `application/x-www-form-urlencoded` body of the form `key1=value1&key2=value2`
    */
    - (NSData *)httpBodyForParameters:(NSDictionary *)parameters {
    NSMutableArray *parameterArray = [NSMutableArray array];

    [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
    NSString *param = [NSString stringWithFormat:@"%@=%@", [self percentEscapeString:key], [self percentEscapeString:obj]];
    [parameterArray addObject:param];
    }];

    NSString *string = [parameterArray componentsJoinedByString:@"&"];

    return [string dataUsingEncoding:NSUTF8StringEncoding];
    }

    and

    /** Percent escapes values to be added to a URL query as specified in RFC 3986.

    See http://www.ietf.org/rfc/rfc3986.txt

    @param string The string to be escaped.
    @return The escaped string.
    */
    - (NSString *)percentEscapeString:(NSString *)string {
    NSCharacterSet *allowed = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"];
    return [string stringByAddingPercentEncodingWithAllowedCharacters:allowed];
    }

Post image and JSON data with one NSURLrequest

Your Django web service expects a multipart/form-data request with:

  • a Accept: application/json custom header field,
  • image and kiosk parameters.

You know how to achieve this with curl, from the command line, thanks to the following options:

  1. -H/--header <line> to set your custom header,
  2. -X POST to issue a POST request,
  3. -F/--form <name=content> to specify multipart POST data.

In Objective-C with NSURLConnection and NSURLRequest you need to do the equivalent of the above options:

  1. you can set a custom header on your NSURLRequest instance thanks to the addValue:forHTTPHeaderField: method,
  2. you can perform a POST request via [request setHTTPMethod:@"POST"];,
  3. you need to set a properly formatted body string with [request setHTTPBody:body];.

So the question is: how do I manually create a multipart/form-data body string with my image and kiosk parameters?

The RFC and curl --trace-ascii out.txt ... may help you building the body (note that you need 2 boundary sections since you have 2 parameters).

Alternatively you can easily find many samples on how to do that, e.g: Sending Multipart Forms with Objective-C.

At last, AFNetworking is a convenient wrapper around NSURLConnection. It is particularly handy since it provides a ready-to-use multipartFormRequestWithMethod method used to perform multipart POST requests. So you can use it as an alternative.

Append multiple data to httpbody of URLRequest

If you have to append the airports dictionary to the request body, you may want to include it in the Request model itself.

I would suggest updating your RequestModel and make it Encodable.

And include your airports dict as a part of your RequestModel

Something like this

struct RequestModel: Encodable {

let body: Body
let airportsDict: [String:String]
}

struct Body: Encodable {
let name: String
let score: String
let favList: [String]
}

This way your httpBody will have all the data you want to pass.

Hope this helps

How to append something like ?id=1 to a NSMutableURLRequest

The problem is that the question mark in ?timeFrame=n14 is treated as part of the URL's
path and therefore HTML-escaped as %3F. This should work:

let baseUrl = NSURL(string: "http://www.football-data.org")!
let url = NSURL(string: "soccerseasons/" + "\(league.id)" + "/fixtures?timeFrame=n14", relativeToURL:baseUrl)!
let request = NSMutableURLRequest(URL: url)

Alternatively, use NSURLComponents, which lets you build an URL successively from individual components (error checking omitted for brevity):

let urlComponents = NSURLComponents(string: "http://www.football-data.org")!
urlComponents.path = "/soccerseasons/" + "\(league.id)" + "/fixtures"
urlComponents.query = "timeFrame=n14"

let url = urlComponents.URL!
let request = NSMutableURLRequest(URL: url)

Update for Swift 3:

var urlComponents = URLComponents(string: "http://www.football-data.org")!
urlComponents.path = "/soccerseasons/" + "\(league.id)" + "/fixtures"
urlComponents.query = "timeFrame=n14"

let url = urlComponents.url!
var request = URLRequest(url: url)


Related Topics



Leave a reply



Submit