Search code examples
phpiosobjective-cjsonafnetworking-2

How can I POST an NSArray of NSDictionaries inside an NSDictionary without problems?


I do know how to do this, it's fairly simple.

The problem is that it doesn't work.

Here's the function I use to POST the data:

- (void)updateWebsitesUsingParameters:(NSDictionary *)parameters;
{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    [manager POST:@"http://notreal/updateWebsites.php"
       parameters:parameters
          success:^(AFHTTPRequestOperation *operation, id responseObject) {

              NSLog(@"JSON: %@", responseObject);

              //...
          }
          failure:^(AFHTTPRequestOperation *operation, NSError *error) {

              //...
          }];
}

Here are the parameters:

NSDictionary *parameters = @{@"type" : @"0",
                             @"credentials" : @{@"email" : @"[email protected]", @"password" : @"notreal"},
                             @"device" : @{@"ID" : @"8588107756600540", @"numberOfSessions" : @"0", @"name" : @"Nick's iMac"},
                             @"websites" : @[@{@"title" : @"Google", @"URL" : @"http://www.google.com"}, @{@"title" : @"Yahoo", @"URL" : @"http://www.yahoo.com"}]};

Here's what gets saved in the MySQL field:

[{URL = "http://www.google.com";},{title = Google;},{URL = "http://www.yahoo.com";},{title = Yahoo;}]

THIS IS CRAZY!

  1. I have successfully saved JSON of an array of dictionaries with multiple attributes inside a dictionary inside a MySQL field --or in short what I'm trying to do here-- using a PHP script for a different purpose and it works, no problem.
  2. I use the same PHP code to save it to the MySQL field so IT'S NOT PHP'S FAULT.
  3. All other save / retrieve functions I have made using AFNetworking work perfectly.

This works:

@[@{@"title" : @"Google"}, @{@"title" : @"Yahoo"}]

This doesn't:

@[@{@"title" : @"Google", @"URL" : @"http://www.google.com"}, @{@"title" : @"Yahoo", @"URL" : @"http://www.yahoo.com"}]

Here's the response:

{
    websites =     (
                {
            URL = "http://www.google.com";
        },
                {
            title = Google;
        },
                {
            URL = "http://www.yahoo.com";
        },
                {
            title = Yahoo;
        }
    );
}

INSANE!

For some reason, it breaks down if I add an extra attribute.

This must be an AFNetworking bug because it makes no sense.

EDIT:

I could:

  1. Make two MySQL fields: websiteTitles, websiteURLs.

  2. Save it as one string: "Google;http://www.google.com" and then separate it but that defeats the purpose of using JSON.

  3. Send the parameters chopped in half: websteTitles, websiteURLs

All are hideous, any ideas?

EDIT 2:

I run a few tests:

It doesn't matter if the array has 1 or 2 items it still behaves like this.

I tried what rob180 suggested and --as expected-- it's AFNetwokring's fault:

{
    websites =     (
                {
            URL = "http://www.google.com";
        },
                {
            title = Google;
        },
                {
            URL = "http://www.yahoo.com";
        },
                {
            title = Yahoo;
        }
    );
}

This is the actual server response of what has been send from the app, no mysql in the middle.

EDIT 3:

REQUEST: <NSMutableURLRequest: 0x7f9352d467e0> { URL: http://notreal/updateWebsites.php }

The HTTPBody looks like this:

<63726564 656e7469 616c735b ... 653d30>

How can I decode this?

Also, I'm using an AFHTTPRequestSerializer. Maybe, if I change it to AFJSONRequestSerializer it will fix the problem but I really don't want to since I have written many methods this way.


Solution

  • "query string parameterization is simply not a reliable way of encoding nested data structures. This is why all modern web frameworks have built-in conveniences that automatically decode incoming JSON into parameters." - Mattt Thompson

    So, JSON it is...

    Parameters:

    NSDictionary *parameters = @{@"websites" : @[@{@"Title" : @"Google", @"URL" : @"http://www.google.com"}, @{@"Title" : @"Yahoo", @"URL" : @"http://www.yahoo.com"}]};
    

    Send:

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        manager.responseSerializer = [AFJSONResponseSerializer serializer];
    
        [manager POST:@"http://server/path/file.php"
           parameters:parameters
              success:^(NSURLSessionDataTask *task, id responseObject) {
    
                  NSLog(@"JSON: %@", responseObject);
              }
              failure:^(NSURLSessionDataTask *task, NSError *error) {
    
                  NSLog(@"Error: %@", error.description);
              }];
    

    Retrieve:

    <?php
    
    header('Content-type: application/json');
    
    $request = json_decode(file_get_contents('php://input'), TRUE);
    
    $response = ["URLOfTheSecondWebsite" => $request['websites'][1]['URL']];
    
    echo json_encode($response);
    
    ?>
    

    Response:

    {
        URL = "http://yahoo.com";
    }
    

    All done!

    Pure gold.