Swift and JSON driving me crazy - json

I am really getting stuck on this.
I have created a JSON service, that returns data like this:
[
{
"docNameField": "Test",
"docNumField": 22832048,
"docVerField": 1,
"docDataBaseField": "Legal",
"docCheckedOutWhenField": "03/05/2020",
"whereCheckedOutField": "PC0X8J9RD"
}
]
This is Postman output.
No matter how I try, I cannot seem to be able to put together the correct combination og HTTP call, deserialization, types and so on to get a list of objects out in the end.
This func below outputs this:
JSON String: Optional("[{\"docNameField\":\"Test\",\"docNumField\":22832048,\"docVerField\":1,\"docDataBaseField\":\"Legal\",\"docCheckedOutWhenField\":\"03/05/2020\",\"whereCheckedOutField\":\"PC0X8J9RD\"}]")
func LoadLockedDocumentsByDocnum(docNum:Int32) {
let json: [String: Any] = ["action":"getCheckedOutDocuments","adminUserName":"\(APPuserName)","adminPassword":"\(APPuserPassword)","adminDomain":"\(APPuserDomain)","applicationKey":"19730905{testKey}","searchTerm":docNum]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
self.documentEntries.removeAll()
let url = URL(string: "https://{URL}//CheckOut")!
var request = URLRequest(url: url)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") //Optional
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
let session = URLSession.shared
let dataTask = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if let resultat = response as! HTTPURLResponse?{
if resultat.statusCode == 200{
if error != nil {
}
else {
print(data!)
if let nydata = data{
print("JSON String: \(String(data: data!, encoding: .utf8))")
}
}
}}
}
dataTask.resume()
}

You seem to have come pretty close. To get a list of objects out, you first need to declare that object:
struct MyResponseObject: Decodable { // please give this a better name
let docNameField: String
let docNumField: Int
let docVerField: Int
let docDataBaseField: String
let docCheckedOutWhenField: Date
let whereCheckedOutField: String
}
And then use a JSONDecoder to deserialise the JSON. Instead of:
print("JSON String: \(String(data: data!, encoding: .utf8))")
write:
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
decoder.dateDecodingStrategy = .formatted(formatter)
do {
// here's your list of objects!
let listOfObjects = try decoder.decode([MyResponseObject].self, from: data!)
} catch let error {
print(error) // an error occurred, you can do something about it here
}

Related

How to add post response string of dictionary values to label while parsing in swift

json post response like this:
{
"response": {
"responseReason": "Successful",
"billDetails": "",
"billerResponse":"{\"customerName\":\"RAM\",\"amount\":193,\"dueDate\":\"2019-11-30\",\"custConvFee\":\"\",\"custConvDesc\":\"\",\"billDate\":\"2019-11-16\",\"billNumber\":\"32224081911191623\",\"billPeriod\":\"NA\",\"billTags\":[],\"fieldName\":\"Service Number\",\"fieldValue\":\"116515M025007621\",\"billerName\":\"EPDCL-Eastern Power Distribution Ltd\"}",
for that i have written code like this:
here i am getting json response but i need customerName amount dueDate values in label
func billerFetchService(){
let parameters = ["billDetails": [
"billerId" : "EPDCLOB00ANP01",
"customerParams" : [["name":"Service Number","value":"116515M025007621"]]]]
let url = URL(string: "https://app.com/fetch_v1/fetch")
var req = URLRequest(url: url!)
req.httpMethod = "POST"
req.addValue("application/json", forHTTPHeaderField: "Contet-Type")
req.addValue("application/json", forHTTPHeaderField: "Accept")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else {return}
req.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: req, completionHandler: {(data, response, error) in
if response != nil {
// print(response)
}
if let data = data {
do{
var json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String: Any]
print("fetching json \(json)")
let billerDetails = json["billerResponse"] as! String
print("fetch only billerdetails \(billerDetails)")
let res = try JSONSerialization.jsonObject(with:Data(billerDetails.utf8)) as! [String: Any]
let billerName = res["customerName"] as? String
print("fetch only EPDCL biller name \(billerName)"
}catch{
print("error")
}
}
}).resume()
}
let billerDetails = json["billerResponse"] as! String here i am getting
Thread 5: Fatal error: Unexpectedly found nil while unwrapping an Optional value
You can try
let billerDetails = json["response"] as! [String:Any]
let value = billerDetails["billerResponse"] as! String
print(value)

Invalid top-level type in JSON write' in swift using Alamofire

I have a String printed like this
["answers": <__NSArrayI 0x7fb0bce08550>(
{
qid = 884;
value = (
fSociety
);
}
)
, "uniqid": t-26963212]
I am converting NSObject to Json using Encoder like this
let realm = try! Realm()
let savedExamResponse = realm.object(ofType: SavedExamResponse.self, forPrimaryKey: id)
answersToSubmit.uniqid = savedExamResponse?.uniqueId
var answerListToSubmit = [QuestionAnswersToSubmit]()
for item in (savedExamResponse?.questionAnswerList)! {
var answerToSubmit = QuestionAnswersToSubmit()
answerToSubmit.qid = item.questionId
answerToSubmit.value.append(item.selectedOption)
answerListToSubmit.append(answerToSubmit)
}
let answersToSubmit = SubmitAnswerModel(answers:answerListToSubmit,uniqid:savedExamResponse?.uniqueId)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try? encoder.encode(answersToSubmit)
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!, options : .allowFragments) as? [String:AnyObject]
{
print(jsonObj) // use the json here
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
I need to send BODY parameter in my API so whenever I try to send this value I get Invalid top-level type in JSON write'.
I am using Alamofire like this
let urlString = UrlCollection.submitAnswerUrl + "uniqid=" + answersToSubmit.uniqid! + "&token=" + token
var objectDictionaries = [NSDictionary]()
let allObjects = answersToSubmit
var request = URLRequest(url: URL(string: urlString)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: data, options: JSONSerialization.WritingOptions.prettyPrinted)
Alamofire.request(request)
.responseJSON { response in
switch response.result {
case .failure(let error):
print(error)
if let data = response.data, let responseString = String(data: data, encoding: .utf8) {
print(responseString)
}
case .success(let responseObject):
print(responseObject)
}
}
I can't figure out what is the error exactly. Is it due to the JSON format not being correct, if then, how should I make this correct. Any help would be deeply appreciated. Thanks
I found the problem in my code
request.httpBody = try? JSONSerialization.data(withJSONObject: data, options: JSONSerialization.WritingOptions.prettyPrinted)
instead of serializing an already encoded value, I passed that value to request.httpBody like this: request.httpBody = data

How to make this POST request with different objects?

Overview:
I am trying to make a POST request, which I have done before with only strings. This time, I have a few variables, being: String, Int, and Bool.
Error:
Cannot assign value of type [String : Any] to type Data
Line causing the error:
request.httpBody = paramToSend
Question:
How to convert a Dictionary into Data ?
Complete Code:
func sendComplimentAPI (message: String, recipient: Int, isPublic: Bool) {
let url = URL(string: "https://complimentsapi.herokuapp.com/compliments/send/")
let session = URLSession.shared
let preferences = UserDefaults.standard
let request = NSMutableURLRequest(url: url!)
request.addValue("\(preferences.object(forKey: "token") as! String)", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
let paramToSend = ["message":message,"recipient":recipient,"is_public":isPublic] as [String : Any]
request.httpBody = paramToSend
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data else {return}
let json:Any?
do{json = try JSONSerialization.jsonObject(with: data!, options: [])}
catch {return}
guard let server_response = json as? NSDictionary else {return}
if let session_data = server_response["id"] as? String {
print("worked")
//do something
/*DispatchQueue.main.async (
execute:
)*/
} else {
print("error")
}
})
task.resume()
}
EDIT:
I have tried this new code and it is still not posting to the server. I am attaching what I changed and also writing what the console shows for the two prints I have it do.
let paramToSend = ["message":writeTextField.text!,"recipient":1,"is_public":isPrivate] as [String : Any] //messageString + recipientString + isPublicString
do {
var serialized = try JSONSerialization.data(withJSONObject: paramToSend, options: .prettyPrinted)
print(serialized)
request.httpBody = serialized
print(request.httpBody)
} catch {
print("found a problem")
}
The console returns (for serialized and then the HTTP body):
113 bytes
Optional(113 bytes)
Is that optional causing the problem? How do I fix it?
To convert Dictionary to Data, use JSONSerialization.data:
Solution:
JSONSerialization.data(withJSONObject: paramToSend, options: .prettyPrinted)
Check the request:
Print the request and see if it matches your expectation
Reading the response:
//Check if there is any error (check if error != nil)
//Examine the response
let statusCode = (response as? HTTPURLResponse)?.statusCode
let statusCodeDescription = (response as? HTTPURLResponse)?.localizedString(forStatusCode: httpResponse.statusCode)
//Check Data
if let data = data {
let dataString = String(data: data, encoding: String.Encoding.utf8)
}
It turns out I needed to add a simple additional header to get the whole thing to work.
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
This is probably why it was not understanding the dictionary I was sending it

HTTP Request GET JSON and read data

i have a problem by a code of me in swift. I do a request to webserver by httpMethod POST. This request is ok. I get a response and data inside the data value. The data looks like JSON
{"pushValues": {"devicePushGlobal":"1","devicePushNewProducts":"1","devicePushNewOffer":"1"}}
Then I will load this response data to set buttons based on the response data. But i fail to write this code. Can someone help me please? :)
Error Code
Cannot invoke 'jsonObject' with an argument list of type '(with: NSString)'
// i tested with other options but i always fail :-(
I comment the error in the code ....
let url = "https://URL.php"
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
let bodyData = "token=" + (dts)
request.httpMethod = "POST"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main) {
(response, data, error) in
// here i get the result of
// {"pushValues": {"devicePushGlobal":"1","devicePushNewProducts":"1","devicePushNewOffer":"1"}}
var str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
var names = [String]()
// here i will get each value of pushValues to add to the array names
do {
if let data = str,
// ... and here is the error code by xcode ::: ==> Cannot invoke 'jsonObject' with an argument list of type '(with: NSString)'
// i tested with other options but i always fail :-(
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let blogs = json["pushValues"] as? [[String: Any]] {
for blog in blogs {
if let name = blog["devicePushGlobal"] as? String {
print(name)
names.append(name)
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
// names array is empty
print(names)
}
Thank you for your help
You shouldn't decode the JSON response into an NSString using var str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue). JSONSerialization.jsonObject() expects a Data object as an input argument, so just safely unwrap the optional data variable and use that as the input argument:
if let responesData = data, let json = try JSONSerialization.jsonObject(with: responseData) as? [String: Any], let blogs = json["pushValues"] as? [String: Any]
The full code using native Swift types:
...
let request = URLRequest(url: URL(string: url)!)
...
URLSession.shared.dataTask(with: request, completionHandler: {
(response, data, error) in
var names = [String]()
do {
if let responseData = data, let json = try JSONSerialization.jsonObject(with: responseData) as? [String: Any], let blogs = json["pushValues"] as? [String: Any]{
if let name = blog["devicePushGlobal"] as? Int {
print(name)
names.append(name)
}
if let newProducts = blog["devicePushNewProducts"] as? Int{}
if let newOffers = blog["devicePushNewOffers"] as? Int{}
}
} catch {
print("Error deserializing JSON: \(error)")
}
// names array is empty
print(names)
}).resume()

Not getting json response on first click using alamofire

I am using alamofire for getting JSON response.
When I click on the button for the first time, I am not getting response. I've checked after few times just to be sure that whether my internet speed is low. Internet speed is okay and still every time this happens, not entering in the if condition to print the response. Please help. Thanks in advance.!!
Below is my code
Alamofire.request(url).responseJSON { response in
if let JSON = response.result.value
{
let responseRes = JSON as? Dictionary<String,AnyObject>
print("Response = \(responseRes!)")
}
}
This will perfectly work in Swift 3.1
func testURL () {
let parameter = ["id": 19, "name": "", "image_name": "", "largeimage": "", "catdata": ["category_name"]] as [String: Any]
//Here parameter as per your web service.
//var parameter = [String : Any]()
//print("t:-\(parameter)")
guard let url = URL(string: "YourWebServiceURL") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameter, options: []) else { return }
request.httpBody = httpBody
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
session.dataTask(with: request) {(data:Data?, response:URLResponse?, error:Error?) in
if let response = response {
print(response)
do {
let json = try JSONSerialization.jsonObject(with: data!) as! [String: Any]
print(json["data"]!)
let dataarray = json["data"]! as! Array<Any>
for i in dataarray {
let webServiceArray = i as! [String : Any]
//Below all the Object as per you webService objects.
print(webServiceArray["name"]!)
print(webServiceArray["largeimage"]!)
print(webServiceArray["image_name"]!)
print(webServiceArray["id"]!)
}
} catch {
print("Error deserializing JSON: \(error)")
}
}
}
.resume()
}
Access this function in ViewDidLoad.