Swift and Alamofire API calls: trouble reading parameters - json

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.

Related

Decoder not decoding json at keypath

Im trying to decode some JSON, but it's not parsing it. I think it may have something to to with either an incorrect KeyPath or the object itself. But I cannot figure it out.
This is the JSON that I want to decode (I want the array inside the docs path):
{
"status": 200,
"data": {
"docs": [
{
"_id": "60418a6ce349d03b9ae0669e",
"title": "Note title",
"date": "2015-03-25T00:00:00.000Z",
"body": "this is the body of my note.....",
"userEmail": "myemail#gmail.com"
}
],
"total": 1,
"limit": 20,
"page": 1,
"pages": 1
},
"message": "Notes succesfully Recieved"
}
Here's my decode function:
extension JSONDecoder {
func decode<T: Decodable>(_ type: T.Type, from data: Data, keyPath: String) throws -> T {
let toplevel = try JSONSerialization.jsonObject(with: data)
if let nestedJson = (toplevel as AnyObject).value(forKeyPath: keyPath) {
let nestedJsonData = try JSONSerialization.data(withJSONObject: nestedJson)
return try decode(type, from: nestedJsonData)
} else {
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Nested json not found for key path \"\(keyPath)\""))
}
}
}
And i'm calling it like this:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let notes = try decoder.decode([Note].self, from: data, keyPath: "data.docs")
Finally, this is my Note Struct:
struct Note: Codable {
var title: String?
let date: Date?
var body: String?
let userEmail: String?
}
The problem was that I was trying to decode date as a Date object instead of a String as is shown on the JSON.
Thanks #vadian!

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]
}

Dictionary to JSON being serialised twice in swift 3

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.

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.