JSONSerialization error in swift3 - json

I am trying to post Audio file to an API, but I am getting an error
"*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (NSURL)'"
Here is my code:
let parameters = [fileUrl]
//create the url with NSURL
let url = NSURL(string: "httpblahblah")
//create the session object
let session = URLSession.shared
//now create the NSMutableRequest object using the url object
let request = NSMutableURLRequest(url: url! as 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)
}
//HTTP Headers
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//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: AnyObject] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()

When you create request.httpBody, you're passing in the parameters array you declared above:
let parameters = [fileUrl]
It's not clear from your code, but if fileUrl is an NSURL instead of a String, that would cause the error you see. The documentation for JSONSerialization says:
All objects are instances of NSString, NSNumber, NSArray,
NSDictionary, or NSNull.
so trying to sneak in an NSURL will definitely not work.
Assuming that your intention is to serialize fileUrl as a String, you can do that by changing the declaration to:
let parameters = [fileUrl.absoluteString!]
If you want to send the actual file data, you're not going to be able to serialize it in a JSON object. Check out this question and its answer for a good example of sending data from a file to a server. Depending on the API you're working with you may still need the JSON object for other data, and its structure will be determined by the API.

From what I understand you are trying to upload a audio file, with a URL reference instead of Data.
Get correct Data format from URL with this:
let data = FileManager.default.contents(atPath: fileUrl.path)
request.httpBody = data

Related

Invalid top-level type in JSON write Swift 4

I am trying to learn JSON parsing. I have written an API in Laravel, which returns status : 200 in response. What I did is this:
guard let url = URL(string: "http://localhost/workon-api/public/api/register") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let newUser = User.init(name: "Rob", email: "abc#gmail.com", password: "12345678")
do {
let jsonBody = try JSONEncoder().encode(newUser)
request.httpBody = jsonBody
} catch { }
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
print(json)
} catch {}
}.resume()
Now, I am getting this error: Invalid top-level type in JSON write and app's crashing. After searching, I used this:
let json = try JSONSerialization.jsonObject(with: data, options: [])
And, it works. Why the previous method is not working? And, I get a response like this if I try to return the collected userInfo.
status = "{\"name\":\"Rob\",\"email\":\"abc#gmail.com\",\"password\":\"12345678\"}";
Why are back-slashes there? Are these okay? And, what is Gzip data? I know I am asking a lot, but I need to understand this. Thanks in advance.
P.S. : Here is the User Model.
struct User: Encodable {
let name : String?
let email : String?
let password : String?
}
First of all the backslashes are virtual. The framework adds them to be able to print double quotes within a literal string.
Secondly dataTask returns serialized JSON Data so to get a dictionary or array from the data you have to call jsonObject(with.
let object = try JSONSerialization.jsonObject(with: data)
print(object)

HTTP Request and JSON parsing in Swift

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!

How to POST data from multiple view controllers to server with JSON using SWIFT

I need help with combining data collected from firstVC, secondVC, and thirdVC and serializing those in the fourthVC.
This link helps with one VC but I have to send only ONE FILE of JSON DATA to the server.
How to create and send the json data to server using swift language
The other method is passing a dictionary array from firstVC, secondVC, and thirdVC to the fourthVC and from the fourthVC convert the dictionaries into JSON. But i don't know how to do that.
I used the format from the answer provided in the link above, but if you need additional info, I will gladly cooperate. Thanks!
PS. Please give me useful comments that will help in any way. I need the code and not feedbacks like doing my own research and such cause I have been stressing about this for nearly a month now.
This is the UserDefault keys
if let AC = UserDefaults.standard.value(forKey: "Acc") as? String {
labeltext.text = "\(AC)"
}
if let TY = UserDefaults.standard.value(forKey: "Taxyear") as? String {
taxtext.text = "\(TY)"
}
if let BB = UserDefaults.standard.value(forKey: "Bsb") as? String {
bsbtext.text = "\(BB)"
}
Here is my JSON code
#IBAction func save(_ sender: Any){
typealias JSONDictionary = [String:Any]
let parameters = ["BankAccountNumber": "Acc", "Tax Year": "Taxyear", "my-bsb": "Bsb"]
let url = URL(string: "https://server:port/")! //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
let valid = JSONSerialization.isValidJSONObject(parameters) // true
print (valid)
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)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// 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] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
let alertMessage = UIAlertController(title: "Saved!", message: "We have recorded your information", preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title:"Okay", style: UIAlertActionStyle.default, handler: nil)
alertMessage.addAction(action)
self.present(alertMessage, animated: true, completion: nil)
}
I solved it by first storing them in a variable
var TITLE = UserDefaults.standard.value(forKey: "Title")
var GN = UserDefaults.standard.value(forKey: "GivenNames")
var LN = UserDefaults.standard.value(forKey: "LastName")
Then I placed them in a parameter and that's done. It was so obvious that I can't believe I didn't solve it sooner
#IBAction func save(_ sender: Any){
let parameters = ["Tax Year": TaxYear, "Title": TITLE, "first-name": GN, "sur-name": LN]

Sending JSON Data with Swift

I'm building a chat app and I understand how to receive the data back but I'm having trouble sending the data. I'm trying to take two UITextField values which are the username and the message and send the data.
Variables
#IBOutlet weak var username: UITextField!
#IBOutlet weak var textBox: UITextField!
Request To Receive
#IBAction func sendMessage(_ sender: UIButton) {
let parameters = ["user": username.text, "message": textBox.text] as! [String: String]
//create the url with NSURL
let url = NSURL(string: "http://website.com/getChatLogJSON.php")
//create the session object
let session = URLSession.shared
//now create the NSMutableRequest object using the url object
let request = NSMutableURLRequest(url: url! as 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)
}
//HTTP Headers
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//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: AnyObject] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
Checkout SwiftyJSON, its a neat library for your JSON needs.
data insertion into the JSON object becomes as simple as:
json["name"] = JSON("new-name")
json[0] = JSON(1)
json["id"].int = 1234567890
json["coordinate"].double = 8766.766
json["name"].string = "Jack"
json.arrayObject = [1,2,3,4]
json.dictionaryObject = ["name":"Jack", "age":25]
Here is the github page, all instructions are there https://github.com/SwiftyJSON/SwiftyJSON
When it contains all data you need to put in, you can return the raw json in a string if you wish and send that which can be parsed
//convert the JSON to a raw String
if let rawString = json.rawString() {
//Do something you want
} else {
print("json.rawString is nil")
}

Global functions to post and get API data in swift

I am trying to make my POST and GET API request methods global so that I need not repeat the call procedure repetitively. For this, I created a swift file and made two functions for POST API and GET API, but can't figure out how to pass data from calling class to the called class, and return back the response to the calling class. My code which needs to be integrated in a separate swift file is :
let request = NSMutableURLRequest(URL: NSURL(string: "http://api.quickblox.com/users.json")!)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("0.1.1", forHTTPHeaderField: "QuickBlox-REST-API-Version")
request.setValue(tokenSet, forHTTPHeaderField: "QB-Token")
request.HTTPMethod = "POST"
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(dict, options: [])
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
I need to pass API Url and a request data in the form of a dictionary, to, say postApiRequest() and getApiRequest() methods. How do I create swift file for the same and call it elsewhere.