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.
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];
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
andkiosk
parameters.
You know how to achieve this with curl, from the command line, thanks to the following options:
-H/--header <line>
to set your custom header,-X POST
to issue aPOST
request,-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:
- you can set a custom header on your
NSURLRequest
instance thanks to theaddValue:forHTTPHeaderField:
method, - you can perform a
POST
request via[request setHTTPMethod:@"POST"];
, - 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
How to Trigger a Block After a Delay, Like -Performselector:Withobject:Afterdelay:
Uialertview Addsubview in Ios7
Having a Uitextfield in a Uitableviewcell
Importing Commoncrypto in a Swift Framework
How to Intercept Touches Events on a Mkmapview or Uiwebview Objects
Class Does Not Implement Its Superclass'S Required Members
Disabling User Selection in Uiwebview
Get Button Click Inside Uitableviewcell
Any Way to Pre Populate Core Data
How Disable Copy, Cut, Select, Select All in Uitextview
How to Change Button Title Alignment in Swift
The Simplest Way to Resize an Uiimage
Wkwebview Origin Null Is Not Allowed by Access-Control-Allow-Origin
Attempt to Present Uiviewcontroller on Uiviewcontroller Whose View Is Not in the Window Hierarchy