Dictionary to JSON being serialised twice in swift 3 - json

I'm trying to make a post request with the following dictionary which is converted to JSON
let store = [
"newTask" : [
"project_name": "iOS",
"total_time":0,
"color":"blue"
]
]
I am serialising this using the following code and then making a http-POST request using the following options:
let jsonData = try? JSONSerialization.data(withJSONObject: store)
var request = URLRequest(url: URL(string: "http://localhost:3000/store")!)
request.httpMethod = "POST"
request.httpBody = jsonData
I am also running a json-server https://github.com/typicode/json-server with the following db.json file
{
"store": [
{
"id": 0,
"ios": {
"project_name": "iOS",
"total_time": 0,
"color": "blue"
}
},
{
"id": 1,
"elm": {
"project_name": "elm",
"total_time": 0,
"color": "blue"
}
}
]
}
The problem I am having is that the newly added item in the db looks incorrect with the following format:
{
"{\"newTask\":{\"project_name\":\"iOS\",\"total_time\":0,\"color\":\"blue\"}}": "",
"id": 10
},
I am unsure as to why it is serialising the whole dictionary as the key and then an empty string as the value.
Update
Here is the code that posts this to the server:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
}
} catch let error {
print(error.localizedDescription)
}
}
task.resume()
As a side note, I've tried pining this via postman and it all works ok. Attaching a screenshot of it below.
Any help will be appreciated, Thanks!

When you construct your URLRequest would this line fix it for you?
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
In Postman you are sending it as application/json so I would expect you need to do the same in your swift code.

Related

SwiftUI: how to add type for JSON with 0 index array key

I am making a API call in swift and the response of the API looks like this:
[
{
"response": {
"data": {
"first": "Hello",
"second": "Hola",
"third": "Namaste"
}
}
}
]
Now to be able to integrate it into my app I need to decode the response from the API call for which the type I am using is as below:
struct APIResponse: Codable {
let response: Response
}
struct Response: Codable {
let data: Data
}
struct Data: Codable {
let first: String
let second: String
let third: String
}
Now the issue is this type is wrong because the API response is actually a array with only one element (index 0), which means the above type would work if the response from API was
[
"response": {
"data": {
"first": "Hello",
"second": "Hola",
"third": "Namaste"
}
}
]
how to define index-0 data in my type?
The code I am using to make API call and decode JSON:
var request = URLRequest(url: URL(string: "https://myurl.com")!)
let (data, _) = try await URLSession.shared.data(for: request)
let decodedResponse = try? JSONDecoder().decode(APIResponse.self, from: data)
Your response is an array of elements:
let decodedResponse = try JSONDecoder().decode([APIResponse].self, from: data)
and:
let element = decodedResponse.first
should get you going.
And never use try? it will obfuscate all errors.

HTTP Post Requests Swift 5 with List of JSON objects

Hello I am using Swift 5 to make an HTTP POST request, the request returns a list of JSON Objects which I believe is causing my error, and I can't seem to find a solution as I have scoured almost every example I could find on stackoverflow and youtube, and Apples documentation, even though this seems like a basic concept.
Here is what my response would look like:
[
{
"_id": "---------------",
"phone_number": "xxx-xxx-xxx"
"first_name": "Name",
"last_name": "LastName",
"gender": "Female",
"verified": true
},
{
"_id": "---------------",
"phone_number": "xxx-xxx-xxx"
"first_name": "Name",
"last_name": "LastName",
"gender": "Female",
"verified": true
}
]
The code I am using to make my HTTP Request is:
let url = URL(string: "URLString")!
let json: [String:Any] = ["phone_number":phoneNumber]
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do{
request.httpBody = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
}catch let error {
print(error.localizedDescription)
}
//HTTP Headers
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//URL data task
let task = session.dataTask(with: request) { (data, response, error) in
guard error == nil else{
print("Error!")
return
}
guard let data = data else{
print("Error fetching data")
return
}
do {
guard let responseJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any] else {
print("Unable to serialize response!") //FAILS HERE EVERY TIME
return
}
print(responseJSON)
print("WOO made it!")
}catch let error {
print("Error with response!")
}
}
task.resume()
I am not sure what the issue is exactly but I have indicated where it fails, and I have tried printing out the data before it converts it just to make sure it isn't empty and it says like 2000 bytes so I know its not empty. My guess is that there is a problem because it is in the format of a list of objects, rather than just one object as most of the examples have shown. I also am having some issues with my HTTP body being nil on my express server end sometimes, so Not sure if I am doing that part wrong. But as of Now I just want to be able to parse my response So I can begin debugging the rest. Thanks for any help.
You need [[String:Any]] instead of [String:Any]
guard let responseJSON = try JSONSerialization.jsonObject(with: data) as? [[String:Any]] else {
print("Unable to serialize response!") //FAILS HERE EVERY TIME
return
}
But it's better using JSONDecoder
do {
let res = try JSONdecoder().decode([Root].self,from:data)
}
catch {
print(error)
}
Correct json
[
{
"_id": "---------------",
"phone_number": "xxx-xxx-xxx",
"first_name": "Name",
"last_name": "LastName",
"gender": "Female",
"verified": true
},
{
"_id": "---------------",
"phone_number": "xxx-xxx-xxx",
"first_name": "Name",
"last_name": "LastName",
"gender": "Female",
"verified": true
}
]
struct Root: Codable {
let id, phoneNumber, firstName, lastName: String
let gender: String
let verified: Bool
enum CodingKeys: String, CodingKey {
case id = "_id"
case phoneNumber = "phone_number"
case firstName = "first_name"
case lastName = "last_name"
case gender, verified
}
}

Error parsing json with Swift4, imposible to print it

I've been working on Swift and it's impossible to parse my JSON. I've created the struct with http://www.jsoncafe.com/ , everything looks great, optional values, coding keys, etc. But all the time I get this Error.
Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
This is my code.
func fetchData() {
guard let gitUrl = URL(string: "https://www.zaragoza.es/sede/servicio/farmacia.json?tipo=guardia") else { return }
URLSession.shared.dataTask(with: gitUrl) { (data, response
, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let guardia = try decoder.decode([Result].self, from: data)
print(guardia.first?.title as Any)
} catch let err {
print("Error", err)
}
}.resume()
}
JSON:
{ "totalCount": 12,
"start": 0,
"rows": 50,
"icon": "farmaciaguardia",
"result": [
{
"id": 8747,
"title": "Farmacia De Miguel Golvano, Cristóbal",
"telefonos": "976220481",
"horario": "Lunes a Sábado excepto festivos de 9:30 a 22:00 h",
"clasificacion": "HorarioAmpliado",
"calle": "Pº de Sagasta, 13",
"geometry": {
"type": "Point",
"coordinates": [
-0.8857963286144336,
41.643332650243835
]
},
"guardia": {
"fecha": "2019-04-20T00:00:00Z",
"turno": "T-05",
"horario": "Abiertas de 9:15 h. a 9:15 h. del día siguiente",
"sector": "Sector Centro-Esquina C/ Bolonia"
},
"type": [
"http://www.zaragoza.es/sede/portal/skos/vocab/FarmaciaGuardia/2019-04-20",
"http://www.zaragoza.es/sede/portal/skos/vocab/FarmaciaHorarioAmpliado"
]
}]}
Finally get it.
There is a problem with headers. This is the answer. Thanks a lot.
func fetchData() {
let url = URL(string: "https://www.zaragoza.es/sede/servicio/farmacia.json?tipo=guardia")
var request = URLRequest(url: url!)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") // the request is JSON
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
let jsonDecoder = JSONDecoder()
let responseModel = try jsonDecoder.decode(Result.self, from: data!)
print(responseModel)
} catch {
print("Error: \(error.localizedDescription)")
}
}
task.resume()
}
You’re telling the decoder that your JSON response has a top level element of an array but it doesn’t. The array is in a nested property called result. You need to create something like
struct ResultToDecode: Decodable {
let result: [Result]
}

Swift and Alamofire API calls: trouble reading parameters

I am using Alamofire 4 and Swift 4. I am attempting to make an API request that is passed 1 parameter called Body which is a JSON string. The JSON string should look like this:
{
"BirthDate": "1985-02-08",
"GivenName": "mary",
"FamilyName": "lee",
"LicenceNumber": "94977000",
"StateOfIssue": "ACT"
}
My code is returning the following result in the debug console:
{
"result" : {
"statuscode" : "400",
"error" : [
"GivenName needs to be a string",
"FamilyName needs to be a string",
"BirthDate needs to be a string",
"LicenceNumber needs to be a string",
"StateOfIssue needs to be a string",
"MiddleName needs to be a string",
"GivenName needs a value",
"FamilyName needs a value",
"BirthDate needs a value",
"LicenceNumber needs a value",
"StateOfIssue needs a value"
]
}
}
My code is as follows:
public func testApi(){
let headers = ["token":self.APIkey, "Content-Type": "application/x-www-form-urlencoded"]
let url = self.testApiUrl
let bodyString : String = "{\"BirthDate\":\"1985-02-08\",\"GivenName\":\"mary\",\"FamilyName\":\"lee\",\"LicenseNumber\":\"94977000\",\"StateOfIssue\":\"ACT\"}"
let params : [String:String] = ["Body":"\(bodyString)"]
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.responseJSON { response in
if let apiResponse = response.result.value as? [String : AnyObject] {
print("params is \(params)")
if apiResponse["exceptionId"] as? String == nil {
print(apiResponse)
return
}
}
}
}
Could someone please help? I have tried breaking down the Body string to a dictionary level (e.g. Body: [name:Mary ... etc]) but this did not work either, and the API docs say it should be passed 1 parameter called Body which is a string.
try below code
public func testApi(){
//replace "application/x-www-form-urlencoded" with "application/json"
//let headers = ["token":self.APIkey, "Content-Type": "application/x-www-form-urlencoded"]
let headers = ["token":self.APIkey, "Content-Type": "application/json"]
let url = "https://sandbox.rapidid.com.au/dvs/driverLicence"
let bodyString: [String: Any] = [
"BirthDate": "1985-02-08",
"GivenName": "mary",
"FamilyName": "lee",
"LicenceNumber": "94977000",
"StateOfIssue": "ACT"
]
//I think you don't want "Body" in request
//let params : [String: Any] = ["Body": bodyString]
//replace params with bodyString
Alamofire.request(url, method: .post, parameters: bodyString, encoding: JSONEncoding.default, headers: headers)
.responseJSON { response in
if let apiResponse = response.result.value as? [String : AnyObject] {
print("params is \(bodyString)")
if apiResponse["exceptionId"] as? String == nil {
print(apiResponse)
return
}
}
}
}
Usually when a POST request is made by passing JSON data, the data is passed in the body.
...API docs say it should be passed 1 parameter called Body which is a string
If I understand correctly then the API is expecting JSON Data in the body of the post request.
Looks like you haven't encoded the JSON correctly. Here's how to fix it...
public func testApi() {
let headers = ["token":self.APIkey, "Content-Type": "application/x-www-form-urlencoded"]
let url = self.testApiUrl
let bodyString: [String: Any] = [
"BirthDate": "1985-02-08",
"GivenName": "mary",
"FamilyName": "lee",
"LicenceNumber": "94977000",
"StateOfIssue": "ACT"
]
let params : [String: Any] = ["Body": bodyString]
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.responseJSON { response in
if let apiResponse = response.result.value as? [String : AnyObject] {
print("params is \(params)")
if apiResponse["exceptionId"] as? String == nil {
print(apiResponse)
return
}
}
}
}
Hopefully this fixes it.
However if you need to pass the JSON as a parameter then you need to encode it into the URL. Check out this answer for that.

Parse twitter trend api results in swift

I using twitter trend api to get the name of all the trends.
I have the following setup:
let url = "\(APIConstants.Twitter.APIBaseURL)1.1/trends/place.json?id=1"
let client = TWTRAPIClient()
let statusesShowEndpoint = url
let params = ["id": "20"]
var clientError : NSError?
let request = client.urlRequest(withMethod: "GET", url: statusesShowEndpoint, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if connectionError != nil {
print("Error: \(connectionError)")
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
}
}
and after the call, json has the following data:
(
{
"as_of": "2012-08-24T23:25:43Z",
"created_at": "2012-08-24T23:24:14Z",
"locations": [
{
"name": "Worldwide",
"woeid": 1
}
],
"trends": [
{
"tweet_volume": 3200,
"events": null,
"name": "#GanaPuntosSi",
"promoted_content": null,
"query": "%23GanaPuntosSi",
"url": "http://twitter.com/search/?q=%23GanaPuntosSi"
},
{
"tweet_volume": 4200,
"events": null,
"name": "#WordsThatDescribeMe",
"promoted_content": null,
"query": "%23WordsThatDescribeMe",
"url": "http://twitter.com/search/?q=%23WordsThatDescribeMe"
},
{
"tweet_volume": 2200,
"events": null,
"name": "Sweet Dreams",
"promoted_content": null,
"query": "%22Sweet%20Dreams%22",
"url": "http://twitter.com/search/?q=%22Sweet%20Dreams%22"
}
]
}
)
From the above json data, i want to store all the name inside trends in an array in swift.
There is some data checking and casting you need to do to make sure you're getting back the data in the structure you'd expect, and then from there its simple iteration. Something along these lines should get you going:
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:Any]
var names = [String]()
if let trends = json?["trends"] as? [[String:Any]] {
for trend in trends {
if let name = trend["name"] as? String {
names.append(name)
}
}
}
Note the various as? type checks to iterate over the structure safely. For a more in-depth review of cleanly working with JSON in Swift including reading the data in to type-safe structs, check out this official blog post from Apple.