Jonathan Thurft
Jonathan Thurft

Reputation: 4173

AFNetworking sending NSDictionary as single array values instead of sending them as complete objects

I'm trying to send a NSArray *appData as it is a NSArray that contains a NSDictionary (See data layout below) but when I receive the Array in PHP all the values in NSDictionary are sent as single Arrays and not in the same format they were sent (See below PHP log data).

All other values in NSDictionary* params are sent and received correctly.

Is this a bug or a problem in my code? if the later is the issue, What am i doing wrong?

Note: I am using the laster version of AFNetworking

EDIT: As a result of @dpassage answer I edited to include a more extensive test and to send the information as form-data. The interesting thing around the test is that the @"test" param sent to the server is showing great in PHP but not the @"share":appData its very strange

- (void) checkForUploadEligibility {
   NSArray* dates = [myModel getStatsMaxAndMinResults];
    if ( dates ) {
        NSDictionary* d = [dates objectAtIndex:0];
        NSDate* minDate = d[@"minDate"];
        NSDate* maxDate = d[@"maxDate"];
        NSTimeInterval secondsBetween = [maxDate timeIntervalSinceDate:minDate];

        int numberOfDays = secondsBetween / 86400;
        numberOfDays = abs(numberOfDays);

        if (numberOfDays >= 0) {
            myUser = [myModel getCurrentUser];
            if ( myUser.userWebID && myUser.authToken ) {
                NSArray* appData = [myModel getAllStatsData];
                if (!appData) {
                    return;
                }
                NSDictionary* params = @{  @"userID": myUser.userWebID,
                                       @"authCode": myUser.authToken,
                                       @"interact":@{@"action":@"uploadAppStats",
                                                     @"actionTarget":@"uploadAppStats"},
                                       @"share":appData,
                                       @"test":@[@{@"test1":@{@"test2":@"test2"}},@{@"test3":@"test3"}]
                                       };
                [myShare uploadStats:params statsData:appData];
            }

        }
    }
}
- (void) uploadStats:(NSDictionary*)params statsData:(NSArray *)statsData {
    NSLog(@"count %i, %@",[statsData count],statsData);
    NSURL *url = [[NSURL alloc]initWithString:sysURL];
    AFHTTPClient *httpClient = [[AFHTTPClient alloc]initWithBaseURL:url];

    NSURLRequest *request = [httpClient requestWithMethod:@"POST" path:sysAppURLPath parameters:params];

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
                                                                                        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON){
                                                                                            NSLog(@"a: Inside the success block %@",JSON);
                                                                                            if ( [JSON objectForKey:@"ok"] ) {
                                                                                            //    [myModel deleteAllStatsData:statsData];
                                                                                            }

                                                                                        }
                                                                                        failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){

                                                                                            NSLog(@"json text is: %@", JSON);
                                                                                            NSLog(@"Request failed with error: %@, %@", error, error.userInfo);
                                                                                        }];
    // Debug HTTP response
    BOOL dbug = YES;
    if (dbug) {
        [operation  setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            NSLog(@"HTTP r: %@",  operation.responseString);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"HTTP Error: %@",  operation.responseString);
        }
         ];
    }
    [operation start];

}

Error log

count 5, (
        {
        date = "2013-09-09 16:10:51 +0000";
        name = selectRoutine;
        shared = 0;
        timeSpent = 3;
    },
        {
        date = "2013-09-09 16:10:53 +0000";
        name = manageExercises;
        shared = 0;
        timeSpent = 1;
    }, // MORE.....



   HTTP Error: array(24) {
      multipart/form-data; boundary=Boundary+0xAbCdEfGbOuNdArY
array(5) {
  ["authCode"]=>
  string(32) "e186cdd000a741ef76555009d7e86d80"
  ["interact"]=>
  array(2) {
    ["action"]=>
    string(14) "uploadAppStats"
    ["actionTarget"]=>
    string(14) "uploadAppStats"
  }
  ["share"]=>
  array(15) {
    [0]=>
    array(1) {
      ["date"]=>
      string(19) "2013-09-09 19:52:31"
    }
    [1]=>
    array(1) {
      ["name"]=>
      string(13) "selectRoutine"
    }
    [2]=>
    array(1) {
      ["timeSpent"]=>
      string(1) "3"
    }
    [3]=>
    array(1) {
      ["date"]=>
      string(19) "2013-09-09 19:52:33"
    }
    [4]=>
    array(1) {
      ["name"]=>
      string(14) "manageRoutines"
    }
    [5]=>
    array(1) {
      ["timeSpent"]=>
      string(1) "2"
    }
    [6]=>
    array(1) {
      ["date"]=>
      string(19) "2013-09-09 22:24:31"
    }
    [7]=>
    array(1) {
      ["name"]=>
      string(13) "selectRoutine"
    }
    [8]=>
    array(1) {
      ["timeSpent"]=>
      string(2) "19"
    }
    [9]=>
    array(1) {
      ["date"]=>
      string(19) "2013-09-09 22:24:45"
    }
    [10]=>
    array(1) {
      ["name"]=>
      string(14) "manageRoutines"
    }
    [11]=>
    array(1) {
      ["timeSpent"]=>
      string(2) "14"
    }
    [12]=>
    array(1) {
      ["date"]=>
      string(19) "2013-09-09 22:25:58"
    }
    [13]=>
    array(1) {
      ["name"]=>
      string(15) "manageExercises"
    }
    [14]=>
    array(1) {
      ["timeSpent"]=>
      string(2) "73"
    }
  }
  ["test"]=>
  array(2) {
    [0]=>
    array(1) {
      ["test1"]=>
      array(1) {
        ["test2"]=>
        string(5) "test2"
      }
    }
    [1]=>
    array(1) {
      ["test3"]=>
      string(5) "test3"
    }
  }
  ["userID"]=>
  string(24) "5224b598f86f265801000008"
}

Upvotes: 3

Views: 1888

Answers (2)

Jonathan Thurft
Jonathan Thurft

Reputation: 4173

The Problem:

NSArray* appData that is inserted into @"share":appData somehow is translated into a multitude of little arrays.

The solution:

Somehow if I insert a NSArray to the Key-Value NSDictionary @"share" it doesn't work as expected. Then I went on changed that NSArray into a NSDictionary that didn't work either.

What worked is the code below. I had to recreate NSArray* appData as NSDictionary* appData2. Then I had to wrap what would be each individual NSDictionary data object into another NSDictionary but I had to give this new object an explicit numeric index... After doing that it would then send the information correctly.

I guess that my problem is a bug? I don't really know... but if anyone has the problem too, this could help you.

        NSArray* appData = [myModel getAllStatsData];
        if (!appData) {
            return;
        }

        NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
        [dateFormat setDateFormat: @"yyyy-MM-dd HH:mm:ss"];
        NSMutableDictionary* choa = [[NSMutableDictionary alloc]init];
        int i = 0;
        for (StatsAppUsage*statObj in appData) {

        [choa addEntriesFromDictionary:@{[NSNumber numberWithInteger:i]:@{@"date":[dateFormat stringFromDate:statObj.date],
                                                          @"name":statObj.name,
                                                          @"timeSpent": statObj.timeSpent
                                                          }
                          }];
            i++;
        }
        NSDictionary* appData2 = [[NSDictionary alloc]initWithDictionary:choa];
        NSDictionary* params = @{  @"userID": myUser.userWebID,
                                   @"authCode": myUser.authToken,
                                   @"interact":@{@"action":@"uploadAppStats",
                                                 @"actionTarget":@"uploadAppStats"},
                                   @"share":appData2
                                   };

@"share" PHP output

(
    [0] => Array
        (
            [date] => 2013-09-09 19:52:31
            [name] => selectRoutine
            [timeSpent] => 3
        )

    [1] => Array
        (
            [date] => 2013-09-09 19:52:33
            [name] => manageRoutines
            [timeSpent] => 2
        )

Upvotes: 2

dpassage
dpassage

Reputation: 5453

I think the issue comes about because you're using a default HTTP POST with arguments to send your value. You can confirm this by printing out the content-type of the post on the server side; it should be something along the lines of x-www-form-urlencoded. If that's the case, that data format can't really express arrays and dictionaries well. So you get the odd format you're seeing on the server side.

What you probably want to do is send your data as JSON, not as plain form parameters. I believe the thing you want to do is set the parameterEncoding property of your AFHTTPClient to AFJSONParameterEncoding. This will send your request as JSON, which is capable of representing arrays and dictionaries correctly on the wire.

Upvotes: 0

Related Questions