Swift: send POST request with JSONEncoder encoded data does not work - json

I am using the JSONEncoder that has been provided with Swift4.
I have a class called Customer that uses the Codable protocol. Inside of Customer there are four Strings.
class Customer: Codable {
var title: String
var firstName: String
var lastName: String
var email: String
}
Reading JSON with a GET Request works fine.
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
let customer = try JSONDecoder().decode(Customer.self, from: data)
DispatchQueue.main.async {
print("\(customer.title)")
print("\(customer.firstName)")
print("\(customer.lastName)")
print("\(customer.email)")
}
} catch let jsonError {
print(jsonError)
}
}.resume()
However when I start to do a POST request I am lost:
First set up the request object:
let urlString = "http://localhost:8000/customer"
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
let customer = Customer()
customer.title = "Mr"
customer.firstName = "Chuck"
customer.lastName = "Norris"
customer.email = "chuck.norris#awsome.com"
let encodedData = try? JSONEncoder().encode(customer)
print(String(data: encodedData!, encoding: .utf8)!) //<- Looks as intended
// Output is {"firstName":"Chuck","lastName":"Norris","title":"MR","email":"chuck.norris#awesome.com "}
Now send it out
request.httpBody = encodedData //
URLSession.shared.dataTask(with: request, completionHandler: {(data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.data(withJSONObject: data, options: [])
print(json)
}catch {
print(error)
}
}
})
In return I receive the message "The given data was not valid JSON."
So, my assumption is that I simply can not just put the encoded JSON data into the http body of my request.
Browsing through some articles about URLSession and JSON I found that it seems that I need to serialize my encoded JSON:
var json: Any?
if let data = encodedData {
json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
}
Now, I don't know how to proceed. First I do not understand why I should serialize my JSON to something that can not simply put into a httpBody.
Because JSONSerialization.jsonObject produces Any? and not Data.
Update
Now, I was able to successfully send my data to my server. I am still trying to understand what was wrong - because I did not changed anything (except for the removal of the JSONSerialization call inside the completion Handler. I will investigate further...
...And .resume had been missing. :-)

Related

How else can I format the data? swift parse json - The data couldn’t be read because it isn’t in the correct format

The JSON data is
[
{"id":0,"temperature":77,"humidity":0.22,"timeCaptured":"2020-09-25T19:33:27.9733333"},
{"id":0,"temperature":77,"humidity":0.22,"timeCaptured":"2020-09-25T20:38:53.3"},
{"id":0,"temperature":85,"humidity":0.25,"timeCaptured":"2020-09-25T20:38:53.3"},
{"id":0,"temperature":88,"humidity":0.22,"timeCaptured":"2020-09-28T15:30:00"},
// ...
]
My structs look like this
struct TemperatureDataModel: Codable{
let id: Int?
let temperature: Double?
let humidty: Double?
let timeCaptured: String?
}
My function looks like this
func getTemperData(){
//Create the URLs
let temperatureDataUrl = URL(string: "https://weatherstationapi.azurewebsites.net/api/TemperatureSensor/GetData")
// let WindDataUrl = URL(string: "https://weatherstationapi.azurewebsites.net/api/WindData/GetAllData")
guard let requestURLTemp = temperatureDataUrl else { fatalError() }
//Create URL request
var request = URLRequest(url: requestURLTemp)
//Specifiy HTTP Method to use
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiaWxpci50YWlyaUB0dHUuZWR1IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiI4MjEzYzhhMy1iODgxLTQ4NmUtOGUyMC1mZmNlMDlmNGY0ZjgiLCJuYmYiOiIxNjAyNTI2NDI1IiwiZXhwIjoiMTYwNTExODQyNSJ9.t1qnYyXLpRRJ3YQfhgLrylBqL_pdKOnKVMgOfG9IuVc", forHTTPHeaderField: "Authorization")
//Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else {return}
print(data)
//Use parseJSON to convert data
let TemperatureData = parseJSON(data: data)
// for singleValue in TemperatureData {
// print(singleValue.temperautre)
// }
//read list
guard let TemperatureDataModel = TemperatureData else {return}
print("Temperature is : \(TemperatureDataModel.temperature)")
// Check if error took place
if let error = error {
print("Error took place \(error)")
return
}
//Read HTTP Response Status Code
// if let data = data, let dataString = String(data: data, encoding: .utf8) {
// print("Response data string:\n \(dataString)")
// }
}
task.resume()
}
and then my JSON decoder function looks like this
func parseJSON(data: Data) -> TemperatureDataModel? {
var returnValue: TemperatureDataModel?
do {
let returnValue = try JSONDecoder().decode([TemperatureDataModel].self, from: data)
} catch {
print("Error took place \(error).")
}
return returnValue
}
I've looked at 6+ stack overflow posts now and still cannot figure It out. Ive tried putting my model in [] for an array, moving where the function is called, changing the jsondecoder function and more and nothing works.
I think you have to give a format to the date before you parse the data
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
Your issue there is that you are creating another returnValue that is not being returned. You need also change the return type to [TemperatureDataModel]
func parseJSON(data: Data) -> [TemperatureDataModel]? {
do {
return try JSONDecoder().decode([TemperatureDataModel].self, from: data)
} catch {
print("Error took place \(error).")
return nil
}
}

How I can pass json data with a decodable struct to UserDefaults properly

I'm a student and I am trying to get a Json Data from an Http Server, and after that save it using UserDefaults
I saw some examples and this code I made seemed to make sense even though it did not work
That is my struct I'm using to decode the json Data
struct UserLogged : Decodable {
var token: String
var userId: String
}
And this is the code I'm trying to use
guard let baseUrl = URL(string: "http://LOCALHOST:8080/auth/login") else {
return
}
var request = URLRequest(url: baseUrl);
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST";
request.httpBody = jsonData;
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
if let user : UserLogged = try JSONDecoder().decode(UserLogged.self, from: data) {
UserDefaults.standard.set(user, forKey: "userLogged")
}
} catch {
print(error)
}
}
}.resume()
You can parse that json with a decoder easily instead of JSONSerialization. It is better to save the token of user in UserDefaults instead of whole user, with a key that makes sense such as userToken:
if let data = data {
print(data)
let decoder = JSONDecoder()
do {
let user = try decoder.decode(UserLogged.self, from: data)
//now you parsed the json and get token and userId
UserDefaults.standard.set(user.token, forKey: "userToken")
} catch {
//Parsing error, couldnt parse user.
}
}

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)

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")
}