2

I am trying to pass a date from Xcode to a PHP page (stores the Date to a mysql DB) however NSData doesn't seem to like the format of the Date.

//Format date for mysql & PHP
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *strToday = [dateFormatter  stringFromDate:[NSDate date]];

NSString *strURL = [NSString stringWithFormat:@"http://www.TESTSERVER.com/Items.php?uID=123456789&item1=30&item2=5&startDate=%@&endDate=%@", strToday, strToday];

//strURL=[strURL stringByReplacingOccurrencesOfString:@" " withString:@"%20"];

NSLog(@"%@", strURL);

NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];

NSString *strResult = [[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding];
NSLog(@"%@", strResult);

I have tested the PHP page by entering data manually and it works without any issues. However when I ran the code above it doesn't work, I've isolated the issue to the date and I believe that NSData has a problem with the space in my Date Format (between the Date and Time).

I tried filling the gap with %20 (URL encoding etc.) and the connection is made to the db but the date field is not in the right format so it appears as null in the mysql database.

I'd rather not start parsing strings in my PHP, does anyone know how to fix this?

Below is my PHP code: -

<?PHP

$hostname = "XXX";
$username = "XXX";
$password = "XXX";
$database = "XXX";

$db_handle = mysql_connect($hostname, $username, $password);
$db_found = mysql_select_db($database, $db_handle);


$uID = $_GET['uID'];
$item1 =$_GET['item1'];
$item2 =$_GET['item2'];
$startDate =$_GET['startDate'];
$endDate =$_GET['endDate'];

if ($db_found) {

    $sql = "INSERT INTO Items (uID, item1, item2, startDate, endDate) VALUES  ('$uID','$item1','$item2','$startDate','$endDate');";

    //$cleanSQL = str_replace("%20"," ",$sql);
    $result = mysql_query($sql);

    mysql_close($db_handle);

    print "Records added to the database";

} else {

    print "Database NOT Found ";
    mysql_close($db_handle);
}

?>
thefan12345
  • 136
  • 16
  • I used stringByAddingPercentEscapesUsingEncoding but I'm still getting null values for Date in my DB – thefan12345 Apr 21 '15 at 19:12
  • You should not be putting data in the URL, there are hundreds of bugs if you do that. Make it a POST request and put the NSData in the "body". Read it in PHP with `file_get_contents("php://input")`. You can send just the date string itself or put the string in an NSDictionary with ofher strings and serialize it with JSON (apple provides JSON serialization class and PHP has JSON functions). Finally, you need to handle time zones or else your date will be incorrect by up to 12 hours. – Abhi Beckert Apr 21 '15 at 22:10
  • Please do not use any PHP function prefixed with `mysql_*`. Instead you must at least use `mysqli_*` ones (note the 'i') or far better switch to PDO. Then you should use use `?` placeholders to insert data into the database as Rob suggested in his comment. You can find plenty of tutorials explaining how to use PDO and `?` placeholders. – Abhi Beckert Apr 23 '15 at 06:09

1 Answers1

1

There's a many problems with this code, but it's impossible to diagnose what precisely is causing the problem you describe on the basis of the limited information provided. A few observations:

  1. You definitely need to percent escape the date string. The space is not acceptable in a URL (nor the body of a standard x-www-form-urlencoded request).

  2. If you are inserting data, you should be issuing POST request, not adding this stuff to a URL, in effect issuing GET request.

  3. You should not be issuing request with dataWithContentsOfURL. Not only is that GET, but it's synchronous and doesn't report the error.

  4. If you still have problems, you should observe this request with Charles or similar tool. Observe the same request that you have working via web browser and compare and contrast.

  5. Personally, when I have problems, I temporarily change my PHP code to return the data I passed to it, so I can make sure that everything was received correctly. It's a simple, but effective, way to confirm that everything was received properly.

  6. If you are passing date strings to the server, you generally should use GMT timezone, to avoid problems stemming from the server not knowing what time zone the user's device is located. Likewise, you should use en_US_POSIX locale, to avoid problems with non-gregorian calendars. See Apple Technical Q&A 1480.

Pulling that all together, you end up with something more like:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; // if you REALLY want to use local timezone, eliminate this line, but I'd really advise sticking with GMT when using formatters to create and parse date strings that you're sending to a remote server
NSString *strToday = [dateFormatter  stringFromDate:[NSDate date]];

NSURL *url = [NSURL URLWithString:@"http://www.TESTSERVER.com/Items.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];

NSString *body = [[NSString stringWithFormat:@"uID=123456789&item1=30&item2=5&startDate=%@&endDate=%@", strToday, strToday] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];

[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];  // not necessary, but good practice

NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // use the data/error/response objects here

    if (data) {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"responseString = %@", responseString);
    } else {
        NSLog(@"error = %@", error);
        NSLog(@"response = %@", response);
    }
}];
[task resume];

// don't try to use the string here, because the above happens asynchronously

Frankly, I don't like this overly simplistic mechanism of setting the body (if any of the fields included special characters like + or &, the above wouldn't work). But if the parameters are precisely as you've outlined them in your question, this simplistic solution should work. But, if you want something a little more robust in this regard, see the encodePostParameters method shown in https://stackoverflow.com/a/22887238/1271826.

By the way, I'd suggest considering using AFNetworking, as that gets you out of the weeds of manually constructing requests.

But, going back to your original question, we cannot advise you further without seeing the server code or you doing more debugging (Charles, confirming that the values are correctly being received, etc.).

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I recommend avoiding AFNetworking these days, sometimes it has bugs and the official API for URL loading isn't nearly as "weedy" as it was when AFNetworking was born. – Abhi Beckert Apr 21 '15 at 22:05
  • While I'm not entirely unsympathetic to your point, we'll have to agree to disagree on that point: Yes, there are many questionable aspects of AFNetworking, but I don't think reliability is a key risk. With such a large install-base, bugs are identified very quickly, generally resolved not soon thereafter. And if someone is just getting started in Objective-C networking, I think AFNetworking is a great place to start. But I certainly don't blame you for your reservations. – Rob Apr 21 '15 at 22:24
  • Rob, thank you for taking the time to answer my question in such detail, I learned an incredible amount here. I've also added the PHP code that I'm using, can you let me know if I need to change anything or at least point me in the right direction (tutorial etc.)? – thefan12345 Apr 22 '15 at 17:37
  • 1
    @thefan12345 - You are just taking input from the request and inserting it into SQL. That's dangerous. It leaves you susceptible to SQL injection attacks. You should use `mysqli_real_escape_string` or bind values to the `?` placeholders in the SQL. See point 3 (as demonstrated in the code under point 2) of http://stackoverflow.com/a/20196541/1271826. Also, `mysql` API is deprecated. Use `mysqli` or `PDO`. See http://php.net/manual/en/mysqlinfo.api.choosing.php. – Rob Apr 22 '15 at 17:52
  • @thefan12345 - By the way, I advised in my answer that you should create `POST` request. If you do that, you should obviously replace `$_GET` references in your server code with `$_POST`. – Rob Apr 22 '15 at 18:07