2

I am trying to send a request to an API that requires this format:

{
    "inputs": [
        {
            "data": {
                "image": {
                    "base64": "iVBORw0KGgoAAAIVnFT/DPAAAAAElFTkSuQmCC..."
                }
            }
        }
    ]
}

This is what I tried in Swift 3:

let base64 = ["base64": "1010101..."]

let image: [String: Any] = ["image": base64]

let data: [String: Any] = ["data": image]

var input = [Dictionary<String, Any>]()

input = [data]

let jsonData = try! JSONSerialization.data(withJSONObject: input, options: .prettyPrinted)

I got an invalid request error when trying to hit the endpoint. Not surprising - when I printed it:

let jsonString = try! JSONSerialization.jsonObject(with: jsonData, options: [])

print(jsonString)

It produced a string like this:

(
        {
        data =         {
            image =             {
                base64 = 1010101;
            };
        };
    }
)

For now, I am rolling my own string. It works...but it's ugly as hell and just really not tenable.

let imageAsString = "{\"inputs\": [{\"data\": {\"image\": {\"base64\": \"\(strBase64)\"}}}]}"

My other thought was that I could create a series of embedded objects: a Data object that holds a dictionary; an Image object that holds a Data object; and an Inputs object that holds an array of Data objects.

class Image {
    var base64 = Dictionary<String, String>()
}

class Data {
    var image = Image()
}

class Inputs {
    var inputs = [Data]()
}

But initializing them all and calling JSONSerialization.data doesn't work as this takes a dictionary object.

I'm not really interested in pulling in 3rd party plugins for now. Any ideas on how to convert these nested objects into json using the native iOS libraries?

jtrenton
  • 23
  • 1
  • 4
  • Could it be that you print `input` and not `jsonData` ? – Martin R Feb 16 '18 at 23:49
  • No. But I appreciate you asking. I edited the question to add just how I printed the json data. – jtrenton Feb 16 '18 at 23:58
  • 1
    You did not print the JSON, but the result of converting it back to a Foundation object. – Try `print(String(data: jsonData, encoding: .utf8)!)` and you'll see that your JSON *is* correct. – Martin R Feb 17 '18 at 03:03

1 Answers1

4

Absolutely! Using the Codable protocol and JSONEncoder in Swift 4 will do the trick:

struct ImageObj: Codable {
    let base64: String
}

struct DataObj: Codable {
    let image: ImageObj
}

struct InputObj: Codable {
    let data: DataObj
}

struct InputsContainerObj: Codable {
    let inputs: [InputObj]
}

let imageObj = ImageObj(base64: "abc123")
let dataObj = DataObj(image: imageObj)
let inputObj = InputObj(data: dataObj)
let inputsContainerObj = InputsContainerObj(inputs: [inputObj])

let encoder = JSONEncoder()
do {
    let jsonData = try encoder.encode(inputsContainerObj)
    let jsonString = String(data: jsonData, encoding: .utf8)!

    print(jsonString) //{"inputs":[{"data":{"image":{"base64":"abc123"}}}]}
} catch _ as NSError {

}

If Swift 3 is your only option, then you will have to make JSONSerialization.data work:

let dict = ["inputs": [["data": ["image": ["base64": "abc123"]]]]]
do {
    let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
    let jsonString = String(data: jsonData, encoding: .utf8)!

    print(jsonString)

    /*
    {
        "inputs" : [
            {
            "data" : {
                "image" : {
                    "base64" : "abc123"
                }
            }
            }
        ]
    }
    */

} catch _ as NSError {

}
Jake
  • 13,097
  • 9
  • 44
  • 73
  • I decided this was my excuse to upgrade to Xcode 10 and Swift 4. Your solution for Swift 4 worked wonderfully. Thank you! – jtrenton Feb 17 '18 at 16:24