2

I have an iOS application (Swift) which encodes some data and does a JSONSerialization and creates a JSON object. My code is below, I've done my best to keep it tidy, so I hope it makes sense:

struct Order: Codable {
    let idQty: [FoodIdAndQuantity]
    let collection: String
    let name: String
    let phone: Int
    let doorNum: Int
    let street: String
    let postcode: String
}

struct FoodIdAndQuantity: Codable {
    let itemId: Int
    let qty: Int
}

class CheckoutServer: NSObject, URLSessionDataDelegate {

    var inputValuesForItemAndQuantity = [Int:Int]()
    var idQty = [FoodIdAndQuantity]()
    var collection = String()
    var name = String()
    var phone = Int()
    var doorNum = Int()
    var street = String()
    var postcode = String()

    var request = URLRequest(url: NSURL(string: "http://192.168.1.100/api/AddOrder.php")! as URL)

    func sendToDatabase() {

        for(key,value) in inputValuesForItemAndQuantity {
            idQty.append(FoodIdAndQuantity(itemId: key, qty: value))
        }

        let order = Order(idQty: idQty,collection: collection,name: name,phone: phone,doorNum: doorNum,street: street,postcode: postcode)

        let encodedOrder = try? JSONEncoder().encode(order)

        var json: Any?

        request.httpMethod = "POST"

        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        if let data = encodedOrder {

            json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
            if var json = json {
                if JSONSerialization.isValidJSONObject(json) {
                    do {
                        json = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
                    } catch {
                        print("There was a problem creating the JSON object")
                    }

                } else {
                    print("not valid JSON")
                }
            }
        }

        let postParameters = "json="+String(describing: json!)

        print(String(describing: json!)) //Print JSON for debugging purposes

        request.httpBody = postParameters.data(using: .utf8)

        let defaultSession = URLSession(configuration: URLSessionConfiguration.default)

        let task = defaultSession.dataTask(with: request) { (data, response, error) in

            if error != nil {
                print("Failed to download data at Menu Type Items")
            } else {
                print("Data uploaded")
            }
        }
        task.resume()
    }
}

So the above code does the following:

  • Creates an encodable object called 'order'.
  • Creates a POST request to my API.
  • Passes the encoded JSON object via a POST parameter.

I've printed the json object that gets posted back to the console in XCode and that looks as follows:

{
    collection = Delivery;
    doorNum = 99;
    idQty =     (
                {
            itemId = 17;
            qty = 5;
        },
                {
            itemId = 1;
            qty = 3;
        }
    );
    name = James;
    phone = 012345667;
    postcode = LXU49RT;
    street = Hope Street;
}

Next, I'll move over to my server/API which accepts the POST parameter.

Below is my AddOrder.php page:

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    require_once dirname(__FILE__) . '/DbOperation.php';

    $json = $_POST["json"];

    $db = new DbOperation();

    $json = $db->addOrder($json);

}

And below is my DbOperation addOrder function:

public function addOrder($json) {
    require dirname(__FILE__) .  '/../../dbconnect.php';
    $decoded = json_decode($json);
    $collection = $decoded{"collection"};

    $stmt2 = $pdo->prepare("INSERT INTO TestTable (collection) VALUES (:collection)");
    $stmt2->bindParam(':collection',$collection);
    $stmt2->execute();
}

It's worth noting that, whilst I try to fix this issue, I have created a test table in my Database which simply stores the collection element of the JSON.

The problem I have is, when I run my application and send the data, nothing gets stored in the database, and my apache error.log file says the Column 'collection' cannot be null. So I assume I am handling the POST parameter incorrectly at some point of my PHP. Unless the fault lies at a Swift level, which I'll add the Swift tag to this post if asked by an admin.

The full error is below:

[Wed Feb 28 15:44:55.178184 2018] [:error] [pid 520] [client 192.168.1.46:52400] PHP Fatal error:  Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'collection' cannot be null in /var/www/api/DbOperation.php:111\nStack trace:\n#0 /var/www/api/DbOperation.php(111): PDOStatement->execute()\n#1 /var/www/api/AddOrder.php(16): DbOperation->addOrder(NULL)\n#2 {main}\n  thrown in /var/www/api/DbOperation.php on line 111

What I've tried

I've tried altering my AddOrder.php page to the following:

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    require_once dirname(__FILE__) . '/DbOperation.php';

    //$json = $_POST["json"];

    $json = json_decode(file_get_contents('php://input'),true);

    $db = new DbOperation();

    $json = $db->addOrder($json);

}
3
  • Dunno, but what you show as a JSON object is not JSON. Commented Feb 28, 2018 at 16:59
  • It's what Swift JSON object looks like. I don't know how to work with a JSON object from Swift in PHP. Surely somebody has had this problem before ? Commented Feb 28, 2018 at 17:00
  • When you print that json to be displayed back in the xcode console.... is xcode doing something to 'format' the output to look at? You would really need to somehow echo out the raw json that came in, and have nothing interpret it before looking at it. Commented Feb 28, 2018 at 17:08

3 Answers 3

2

Your swift code doesn't make much sense. You have code that uses a JSONEncoder to encode your swift object into Data. If that succeeds, you then convert the data back to a Swift object using JSONSerialization. if that succeeds, you then use JSONSerialization.data(withJSONObject:options:) to convert your Swift object back to JSON data, and then use String(describing:) your insanely over-processed JSON Data to a string, which is very, very wrong.

Get rid of all that code. Try this instead:

func sendToDatabase() {

    for(key,value) in inputValuesForItemAndQuantity {
        idQty.append(FoodIdAndQuantity(itemId: key, qty: value))
    }

    let order = Order(idQty: idQty,collection: collection,name: name,phone: phone,doorNum: doorNum,street: street,postcode: postcode)

    guard let encodedOrder = try? JSONEncoder().encode(order) else { return }

    request.httpBody = encodedOrder

    let defaultSession = URLSession(configuration: URLSessionConfiguration.default)

    let task = defaultSession.dataTask(with: request) { (data, response, error) in

        if error != nil {
            print("Failed to download data at Menu Type Items")
        } else {
            print("Data uploaded")
        }
    }
    task.resume()
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for this. I've replaced my sendToDatabase function with your answer. Do you have a link/tutorial for the best way to handle the data on the PHP side ? Thanks again.
Nope. I don't know a lot about PHP. Your Swift code was a train-wreck though.
The updated Swift code should actually send well-formed JSON, where the code you posted did not. Try that and see if it fixes your problem.
I'm new to Swift, can you tell lol ;) Seriously, thanks for that. I do appreciate it. I've tried since I applied your code and I get no errors in my error.log of Apache now, but no record being added to my table. I suspect the remaining issues lie somewhere with me handling the data in PHP
The only thing I have had to add to your code above is request.httpMethod = "POST and also request.addValue application/json. I got a time out error which explains why my log files were empty
|
1

The following is not valid code, and should throw a fatal "Cannot use object of type stdClass as array":

$decoded = json_decode($json);
$collection = $decoded{"collection"};

You probably want this:

$decoded = json_decode($json, true);
$collection = $decoded["collection"];

Or this:

$decoded = json_decode($json);
$collection = $decoded->collection;

3 Comments

You see my attempted fix, where I comment out the POST param and use that php://input line, which one of those is correct ? Also, thanks for the help
In your fix, you're calling json_decode() and then passing the result to addOrder(), which does another json_decode()...
I sense that the OP is flailing around, not understanding what he/she is doing.
0

I noticed a json decoding issue with incoming json from Swift 5 to php. Using file_put_contents in my php API, I found the json string from Swift looks like this

[{"Description":"box 3 of 3","Name":"Box S-7","Barcode":"1007","ComponentID":"50","Notes":"ok"}]

which if you convert with json_decode will throw an error, unless you remove the [ and ] from beginning and end of the string, respectively

$new_json = substr($json, 1, -1);
$decode = json_decode($new_json);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.