I am trying to learn Swift. One of my projects is to try to retrieve JSON data from an internal web service (a group of Python CGI scripts) and convert it into a Swift object. I can do this easily in Python, but I am having trouble doing this in Swift. Here is my playground code:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let endpoint: String = "http://pathToCgiScript/cgiScript.py"
let url = NSURL(string: endpoint)
let urlrequest = NSMutableURLRequest(URL: url!)
let headers: NSDictionary = ["User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)",
"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"]
urlrequest.allHTTPHeaderFields = headers as? [String : String]
urlrequest.HTTPMethod = "POST"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlrequest) {
(data, response, error) in
guard data != nil else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("Error calling script!")
print(error)
return
}
do {
guard let received = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as?
[String: AnyObject] else {
print("Could not get JSON from stream")
return
}
print(received)
} catch {
print("error parsing response from POST")
}
}
task.resume()
I know making a 'POST' to retrieve data may look odd, but that is how the system is set up. I keep on getting:
Could not get data from JSON
I checked the response, and the status is 200. I then checked the data's description with:
print(data?.description)
I got an unexpected result. Here is a snippet:
Optional("<0d0a5b7b 22535441 54555322 3a202244 6f6e6522 2c202242 55535922...
I used Mirror, and apparently the type is NSData. Not sure what to make of this. I have tried to encode the data with base64EncodedDataWithOptions. I have tried different NSJSONReadingOptions as well to no avail. Any ideas?
Update:
I used Wireshark to double check the code in the Playground. Not only was the call made correctly, but the data being sent back is correct as well. In fact, Wireshark sees the data as JSON. The issue is trying to turn the JSON data into a Swift object.
I figured out what was wrong. I was casting to the wrong type. This is the new code:
guard let received = try! NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) as? [AnyObject]
The JSON was not returning an array of dictionaries but an array of objects.
Related
I'm making an http post request within the XCTest framework in Xcode for a simple UI test for my app. Within the request, the response is returned. I can't access that response outside of the URLSession task, and I need to because it contains a JWT that must be decoded and used for another http post request.
I've tried researching how to do this, but it is hard to know the right path as I am a beginner in Swift. I've tried creating a new json object and assigning that response to it outside of the URLSession, but it just says that it can't find that response, it is outside of scope.
// make HTTP request
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
let response = responseJSON as! [String:Any]
The expected results are that I now have my response object outside of the http request and I can then decode it. The actual results is the error:
Use of unresolved identifier 'responseJSON'
The request your making is asynchronous. So when you run the line...
let response = responseJSON as! [String:Any]
the network request hasn't finished yet. So you would normally use a completion handler that will be called when the network returns
Here is an example playground:
//: Playground - noun: a place where people can play
import PlaygroundSupport
import UIKit
PlaygroundPage.current.needsIndefiniteExecution = true
func postSomeData(completion: #escaping ([String: Any]?, Error?) -> Void) {
// setup request
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")
let request = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
completion(nil, error)
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
completion(responseJSON, nil)
}
}
task.resume()
}
postSomeData() { response, error in
print(response, error)
PlaygroundPage.current.finishExecution()
}
Output:
Optional(["id": 1, "title": delectus aut autem, "completed": 0, "userId": 1]) nil
I would also suggest that you use Codable for parsing and mapping the JSON response. Here is an intro to using Codable but there are lots of resources available online on the subject.
So, I'm trying to make a very simple watchOS app in XCode. It consists of a button, two labels and a separator between the two labels. It is a digital assistant app, and needs to interface with Dialogflow (https://dialogflow.com).
The button calls the presentTextInputController function, and I want to use that result as a query to my Dialogflow agent.
I need to make an HTTP request, which in JS would look more like this:
{
url:"https://api.api.ai/v1/query",
method:"post",
body:JSON.stringify({query:"userInput",lang:"en-US",sessionID:"yaydevdiner"}),
headers:{
contentType:"application/json; charset=utf-8",
Authorization:"Bearer <auth_token>"
}
}
The response is a JSON object, and I need to access the jsonObject["result"]["speech"] value as a String to use Label.setText()
Everything I've tried has given errors about type Any and other such things. I also haven't been able to do much debugging since the print output isn't showing up in XCode.
I must mention that I'm an extreme beginner to Swift, and I am not good at handling their types and casting and unpacking and things like that.
Could someone show me how I might handle this request and the subsequent processing of the JSON?
Here is my current code:
//HTTP Request
let parameters = [
"query":name![0] as? String,
"lang":"en-US",
"sessionID":"yaydevdiner"
];
//create the url with URL
let url = URL(string: "https://api.api.ai/v1/query")! //change the url
//create the session object
let session = URLSession.shared
//now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer f786fef55008491fb8422cea2be85eb1", forHTTPHeaderField: "Authorization")
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject (with: data, options: .mutableContainers) as? [String:Any] {
self.Response.setText(json["result"]["string"]);
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
Response is a text label.
This code gives me an error saying I should have a question mark between
json["result"] and ["speech"]. When I do this, it gives me another error saying "Type Any has no subscript members".
Ok, I figured it out.
Because XCode automatically makes an iOS app with the watchOS app, I decided to try debugging in the iOS app until I got the HTTP request and JSON parsing right.
Inside the JSONSerialization if statement, I had to add another if statement:
if let result = responseJSON["result"] as? [String:Any]{
self.Response.setText(result!["speech"] as? String ?? "Network error Occurred")
}
Thanks for the help from vadian!
I´m newbie in Swift and I have some problems with parsing JSON using Swift 3 code.
This is my JSON (extract):
[
{
"COD_USUARIO":"4",
"0":"4",
"USUARIO":"PIEDAD",
"1":"PIEDAD",
"CLAVE":"MU\u00d1OZ",
"2":"MU\u00d1OZ",
"ACTIVO":"1",
"3":"1",
"FECHA_ALTA":"2010-12-07 00:00:00",
"4":"2010-12-07 00:00:00",
"FECHA_BAJA":null,
"5":null,
"CIF":null,
"6":null,
"TELEFONO_CASA":"",
"7":"",
"TELEFONO_MOVIL":"",
"8":"",
"EMAIL_TRABAJO":"",
"9":"",
"EMAIL_PARTICULAR":"",
"10":"",
"COLOR":"16777215",
"11":"16777215",
"ADMINISTRADOR":"0",
"12":"0",
"COD_PERSONA":"9",
"13":"9",
"IMPRESORA_ETIQUETAS":"",
"14":"",
"IMP_JUSTIFICANTES":"",
"15":"",
"VER_SESIONES":"0",
"16":"0",
"COD_EMPRESA":"0",
"17":"0",
"FECHA_TRABAJO":null,
"18":null,
"MEMORIZAR_FECHA":"0",
"19":"0",
"AVISOS_PAGOS":"0",
"20":"0",
"AVISOS_COBROS":"0",
"21":"0",
"AVISOS_DIAS":"0",
"22":"0",
"AVISOS_CONTRATOSC":"0",
"23":"0",
"24":"0"
}
]
And this is my code (extract):
let url = URL(string : "http://192.168.0.252:6996/datos/policlinica/webservices/valida.php")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url!)
request.httpMethod = "POST"
let paramToSend = "usu=" + user + "&pass=" + pwd
request.httpBody = paramToSend.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) in
guard let _:Data = data else{
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print (json) //I can see my json in console
let cod_persona = json["COD_USUARIO"]
print (cod_persona)
//error: Type 'Any' has no subscript members
} catch {
print ("error")
return
}
})
task.resume()
I have tried many examples of the internet, but I can not read a specific JSON data. For example, I would like to read the "COD_USUARIO" field and save the data in a variable, but I can not get it to work well for me.
Any advice on this theme or what am I doing wrong (sure many things)
I think your problem is that your json contains array objects, so I would call it like:
json[0]["COD_USUARIO"]
Because COD_USUARIO is in the first array of the json.
I am trying to get a json data from webpage and put it into my app. I have search so many things online and do not find any of them solve my problem.
The sample json is here
[{"id":"1","name":"Clean Archutecture","ISBN":"9780134494166","phone":null,"email":null,"comment":null,"last_update":"2018-03-10 22:53:29","price":"40","type":"sell"},{"id":"2","name":"Math Book","ISBN":null,"phone":null,"email":null,"comment":null,"last_update":"2018-03-10 22:53:54","price":null,"type":"want"},{"id":"3","name":"abc","ISBN":null,"phone":"hi","email":null,"comment":null,"last_update":"2018-03-11 19:58:00","price":"14.5","type":"want"},{"id":"4","name":"asd","ISBN":"1234","phone":"546","email":"dgf#asdc.com","comment":"234","last_update":"2018-03-11 19:59:57","price":"123","type":"want"}]
Swift code:
import Foundation
import UIKit
class Books: NSObject{
let urlRootPath = "http://maichongju.com/dbbs.php"
let method = "GET" //This is design for the php
func getData(type:String){
var result = NSArray()
let urlPath: String = urlRootPath+"?method=GET&size=ALL"
let url: URL = URL(string: urlPath)!
let request: NSMutableURLRequest = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request as URLRequest){
data,response, error in
if error != nil{
print("error:!! \(String(describing: error))")
return
}
do {
result = try JSONSerialization.jsonObject(with: data!, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
print(result )
}catch {
print(error)
}
//print(result)
}
task.resume()
}
}
Error message is
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around
character 0." UserInfo={NSDebugDescription=Invalid value around
character 0.}
I have double check my json data, i put it into online json view and it show perfectly fine. And I really dont know how to fix this problem
You don't need to force cast json response
do like this:
jsonResponse = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
Convert it like this as this is Array of Dictioanry objects
if let responseArray: [[String:Any]] = jsonResponse as? [[String:Any]] {
// DO HERE
}
I hope this will work for you
For safe side you https://github.com/Alamofire/Alamofire for Network request and for JSON : https://github.com/SwiftyJSON/SwiftyJSON
Update:
let urlPath: String = urlRootPath+"?method=GET&size=ALL"
i think is incorrect, In your body you have added POST but in URL you are appending method=GET
request.httpMethod = "POST"
I am getting unexpected values back when i am parsing my json data from my api, i may be doing something wrong here as i'm quite new to swift but i was getting correct values before when i was receiving one "key" but now i have added two i cannot seem to parse the values properly.
This is the json collected from the address my code is receiving, (sorry if its hard to read havn't worked out how to do line breaks yet in my ruby api)(as long as its functional im not too worried at the moment)
{
"ratings":{
"elements":{"Ready Position":[{"description":"Neutral Grip","values":"1,2,3,4,5"},{"description":"Back Straight (Concave ir Convex?)","values":"1,2,3,4,5"},{"description":"Body Low \u0026 Feet a little more than sholder width apart","values":"1,2,3,4,5"},{"description":"Weight on Balls of Feet","values":"1,2,3,4,5"},{"description":"Head Up","values":"1,2,3,4,5"},{"description":"Sholder Blades Close","values":"1,2,3,4,5"},{"description":"Eyes Drilled","values":"1,2,3,4,5"}],"Split Step":[{"description":"Ready Position Conforms","values":"Yes,No"},{"description":"Body Position Low","values":"1,2,3,4,5"},{"description":"Legs Loaded/Prepared","values":"1,2,3,4,5"}]}
},
"comments":{}
}
Now, My swift code looks like this
let playerAPIurl = "http://linkcoachuat.herokuapp.com/api/v1/session/element?organisation=" + userorganisation + "&group=" + urlGroupSelected + "&sport=" + usersport
print(playerAPIurl)
var request = URLRequest(url: URL(string: playerAPIurl)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("ERROR")
}
else{
do {
let json = try JSONSerialization.jsonObject(with: data!) as? [String: AnyObject]
print(json)
And this is the output im getting from this print(json)
Optional({
comments = {
};
ratings = {
};
})
I know i shouldnt be getting anything more in the comments part, but in the ratings part there should be some data?
so after recieving the json and dealing with parsing it i need to access this part of it ["ratings"]["elements"] and after that im all good
thanks in advance and please bare in mine im very new to swift
Thanks
Try the below code. The url used in below code has your JSON data. This code is printing the output correctly.
func testApi(){
let url = URL(string: "https://api.myjson.com/bins/jfccx")
let session = URLSession.shared
let request = URLRequest(url: url!)
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard let data = data, error == nil else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
print(json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}