iOS Send JSON data in POST request using NSJSONSerialization

ninefifteen picture ninefifteen · Mar 5, 2014 · Viewed 9.2k times · Source

I know that there are similar questions posted as I have read though most all of them and still am having problems. I am trying to send JSON data to my server, but I do not think the JSON data is being received. I'm just not sure what I am missing. Below is my code...

Method to send data to server.

- (void)saveTrackToCloud
{
    NSData *jsonData = [self.track jsonTrackDataForUploadingToCloud];  // Method shown below.
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    NSLog(@"%@", jsonString);  // To verify the jsonString.

    NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:http://www.myDomain.com/myscript.php] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
    [postRequest setHTTPMethod:@"POST"];
    [postRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [postRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [postRequest setValue:[NSString stringWithFormat:@"%d", [jsonData length]] forHTTPHeaderField:@"Content-Length"];
    [postRequest setHTTPBody:jsonData];

    NSURLResponse *response = nil;
    NSError *requestError = nil;
    NSData *returnData = [NSURLConnection sendSynchronousRequest:postRequest returningResponse:&response error:&requestError];

    if (requestError == nil) {
        NSString *returnString = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSUTF8StringEncoding];
        NSLog(@"returnString: %@", returnString);
    } else {
        NSLog(@"NSURLConnection sendSynchronousRequest error: %@", requestError);
    }
}

Method jsonTrackDataForUploadingToCloud

-(NSData *)jsonTrackDataForUploadingToCloud
{
    // NSDictionary for testing.
    NSDictionary *trackDictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"firstValue", @"firstKey", @"secondValue", @"secondKey", @"thirdValue", @"thirdKey", nil];

    if ([NSJSONSerialization isValidJSONObject:trackDictionary]) {

        NSError *error;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:trackDictionary options:NSJSONWritingPrettyPrinted error:&error];

        if (error == nil && jsonData != nil) {
            return jsonData;
        } else {
            NSLog(@"Error creating JSON data: %@", error);
            return nil;
        }

    } else {

        NSLog(@"trackDictionary is not a valid JSON object.");
        return nil;
    }
}

And here is my php.

<?php
    var_dump($_POST);
    exit;
?>

The output that I receive from NSLog(@"returnString: %@", returnString); is...

returnString: array(0) {
} 

Answer

Rob picture Rob · Mar 5, 2014

In your PHP, you're grabbing the $_POST variable, which is for application/x-www-form-urlencoded content type (or other standard HTTP requests). If you're grabbing JSON, though, you should retrieve the raw data:

<?php

    // read the raw post data

    $handle = fopen("php://input", "rb");
    $raw_post_data = '';
    while (!feof($handle)) {
        $raw_post_data .= fread($handle, 8192);
    }
    fclose($handle); 

    echo $raw_post_data;
?>

More likely, though, you want to take that JSON $raw_post_data, decode the JSON into an associative array ($request, in my example below), and then build an associative array $response on the basis of what was in the request, and then encode it as JSON and return it. I'll also set the content-type of the response to make it clear it's a JSON response. As a random example, see:

<?php

    // read the raw post data

    $handle = fopen("php://input", "rb");
    $raw_post_data = '';
    while (!feof($handle)) {
        $raw_post_data .= fread($handle, 8192);
    }
    fclose($handle);

    // decode the JSON into an associative array

    $request = json_decode($raw_post_data, true);

    // you can now access the associative array, $request

    if ($request['firstKey'] == 'firstValue') {
        $response['success'] = true;
    } else {
        $response['success'] = false;
    }

    // I don't know what else you might want to do with `$request`, so I'll just throw
    // the whole request as a value in my response with the key of `request`:

    $response['request'] = $request;

    $raw_response = json_encode($response);

    // specify headers

    header("Content-Type: application/json");
    header("Content-Length: " . strlen($raw_response));

    // output response

    echo $raw_response;
?>

This isn't a terribly useful example (just checking to see if the value associated with firstKey was 'firstValue'), but hopefully it illustrates the idea of how to parse the request and create the response.

A couple of other asides:

  1. You might want to include the checking of the status code of the response (since from NSURLConnection perspective, some random server error, like 404 - page not found) will not be interpreted as an error, so check the response codes.

    You'd obviously probably want to use NSJSONSerialization to parse the response:

    [NSURLConnection sendAsynchronousRequest:postRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (error) {
            NSLog(@"NSURLConnection sendAsynchronousRequest error = %@", error);
            return;
        }
    
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
            if (statusCode != 200) {
                NSLog(@"Warning, status code of response was not 200, it was %d", statusCode);
            }
        }
    
        NSError *parseError;
        NSDictionary *returnDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
        if (returnDictionary) {
            NSLog(@"returnDictionary = %@", returnDictionary);
        } else {
            NSLog(@"error parsing JSON response: %@", parseError);
    
            NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"returnString = %@", returnString);
        }
    }
    
  2. I might suggest that you should use sendAsynchronousRequest, as shown above, rather than synchronous request, because you should never do synchronous requests from the main queue.

  3. My example PHP is doing minimal checking of the Content-type of the request, etc. So you might want to do more robust error handling.