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
}
}
Related
I'm trying to filter JSON and get key & value to parse it. Here all JSON values are dynamic. Right now I need to find "type = object" if the type found is true then I need to check value ={"contentType" & "URL"}.
here is my JSON:
{
"date": {
"type": "String",
"value": "03/04/1982",
"valueInfo": {}
},
"Scanner": {
"type": "Object",
"value": {
"contentType": "image/jpeg ",
"url": "https://www.pexels.com/photo/neon-advertisement-on-library-glass-wall-9832438/",
"fileName": "sample.jpeg"
},
"valueInfo": {
"objectTypeName": "com.google.gson.JsonObject",
"serializationDataFormat": "application/json"
}
},
"startedBy": {
"type": "String",
"value": "super",
"valueInfo": {}
},
"name": {
"type": "String",
"value": "kucoin",
"valueInfo": {}
},
"ScannerDetails": {
"type": "Json",
"value": {
"accountNumber": "ANRPM2537J",
"dob": "03/04/1982",
"fathersName": "VASUDEV MAHTO",
"name": "PRAMOD KUMAR MAHTO"
},
"valueInfo": {}
}
}
decode code:
AF.request(v , method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).responseJSON { (response:AFDataResponse<Any>) in
print("process instance id api document view list::::",response.result)
switch response.result {
case .success:
let matchingUsers = response.value.flatMap { $0 }.flatMap { $0. == "object" }
print("new object doc:::", matchingUsers)
guard let data = response.value else {
return
}
print("new object doc:::", matchingUsers)
if let newJSON = response.value {
let json = newJSON as? [String: [String:Any]]
print("new object doc:::", json as Any)
// let dictAsString = self.asString(jsonDictionary: json)
let vc = self.stringify(json: json ?? [])
print("dictAsString ::: dictAsString::::==",vc)
let data = vc.data(using: .utf8)!
do{
let output = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: [String:String]]
print ("demo:::==\(String(describing: output))")
}
catch {
print (error)
}
do {
if let jsonArray = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [String: [String:String]]
{
print("json array::::",jsonArray) // use the json here
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
}
self.view.removeLoading()
case .failure(let error):
print("Error:", error)
self.view.removeLoading()
}
}
How to get specific values from JSON? Any help is much appreciated pls...
Here is code from my playground with your json sample:
import Foundation
let json = """
{
"date": {
"type": "String",
"value": "03/04/1982",
"valueInfo": {}
},
"Scanner": {
"type": "Object",
"value": {
"contentType": "image/jpeg ",
"url": "https://www.pexels.com/photo/neon-advertisement-on-library-glass-wall-9832438/",
"fileName": "sample.jpeg"
},
"valueInfo": {
"objectTypeName": "com.google.gson.JsonObject",
"serializationDataFormat": "application/json"
}
},
"startedBy": {
"type": "String",
"value": "super",
"valueInfo": {}
},
"name": {
"type": "String",
"value": "kucoin",
"valueInfo": {}
},
"ScannerDetails": {
"type": "Json",
"value": {
"accountNumber": "ANRPM2537J",
"dob": "03/04/1982",
"fathersName": "VASUDEV MAHTO",
"name": "PRAMOD KUMAR MAHTO"
},
"valueInfo": {}
}
}
"""
let data = json.data(using: .utf8, allowLossyConversion: false)!
struct ObjectScanner: Decodable {
let contentType: String
let url: String
let fileName: String
}
enum ObjectScannerType {
case object(ObjectScanner)
}
struct Scanner: Decodable {
enum ScannerType: String, Decodable {
case object = "Object"
}
enum CodingKeys: String, CodingKey {
case type, value
}
let scanner: ObjectScannerType
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ScannerType.self, forKey: .type)
switch type {
case .object:
let value = try container.decode(ObjectScanner.self, forKey: .value)
scanner = .object(value)
}
}
}
struct DateResponse: Decodable {
let type: String
let value: String
// let valueInfo // Not enough information in sample for me to decode this object
}
struct Response: Decodable {
enum CodingKeys: String, CodingKey {
case date
case scanner = "Scanner"
}
let date: DateResponse
let scanner: Scanner
}
let decoder = JSONDecoder()
do {
let response = try decoder.decode(Response.self, from: data)
print(response)
} catch {
print("Error decoding: \(error.localizedDescription)")
}
Note: this example is very unforgiving. Any missing value or type that is not supported will lead to a DecodingError. It's up to you to determine all possible types and what is optional and what is not.
I also didn't decode everything nor do I handle the date object to it's fullest
This is as json goes a very complex example. Everything in it is polymorphic: the date, the Scanner, the ScannerDetails, etc. You need to be very careful how you decode this and make sure you handle all possibilities. I would suggest that if you're starting out, you should explore simpler examples.
I also chose to use enums. Not something everyone would chose but its my preference for decoding polymorphic types such as these.
You can read my article about dealing with polymorphic types as well as unknown types here: https://medium.com/#jacob.sikorski/awesome-uses-of-swift-enums-2ff011a3b5a5
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]
}
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.
I'm trying to convert my swift dictionary to Json string but getting strange crash by saying
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (_SwiftValue)'
My code:
let jsonObject: [String: AnyObject] = [
"firstname": "aaa",
"lastname": "sss",
"email": "my_email",
"nickname": "ddd",
"password": "123",
"username": "qqq"
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:String] {
// use dictFromJSON
}
} catch {
print(error.localizedDescription)
}
Please help me!
Regards.
String is not of type AnyObject. Objects are reference types, but String in swift has value semantics. A String however, can be of type Any, so the code below works. I suggest you read up on reference types and value semantic types in Swift; its a subtle but important distinction and its also different from what you expect from most other languages, where String is often a reference type (including objective C).
let jsonObject: [String: Any] = [
"firstname": "aaa",
"lastname": "sss",
"email": "my_email",
"nickname": "ddd",
"password": "123",
"username": "qqq"
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:String] {
print(dictFromJSON)
}
} catch {
print(error.localizedDescription)
}
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.