Displaying JSON data nicely in Swift 3 - swift

I'm quite new to swift and I'm trying to display JSON data received from a server in 3 columns, however, I'd like the text to line up and possibly remove the brackets surrounding each bit of text. I've attached an image below, along with my code.
let u = UserDefaults.standard.value(forKey: "userIP")!
var request = URLRequest(url: URL(string: "http://\(u):3000/logs")!)
request.httpMethod = "POST"
let postString = "LockID=\(lockid)"
request.httpBody = postString.data(using: .utf8)
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)")
DispatchQueue.main.async() {
self.displayMyAlertMessage("response = \(response)")
}
}
let responseString = String(data: data, encoding: .utf8)
// print("responseString = \(responseString)")
if let data = responseString?.data(using: String.Encoding.utf8) {
let resString = JSON(data: data)
if resString["success"].stringValue == "true"
{
// save the data to be manipulated and stored in table somehow
let returned_name = resString["message"].arrayValue.map({$0["name"].stringValue})
let returned_time = resString["message"].arrayValue.map({$0["lockTime"].stringValue})
let returned_type = resString["message"].arrayValue.map({$0["type"].stringValue})
DispatchQueue.main.async() {
self.updatename!.text = "\(returned_name.description)"
self.updateLock!.text = " \(returned_type.description)"
self.updateText!.text = " \(returned_time.description)"
}
}
else if resString["success"].stringValue == "false"
{
DispatchQueue.main.async() {
self.displayMyAlertMessage(resString["message"].stringValue)
}
}
Im maniplulating the JSON data with SwiftyJSON. Any ideas on how i can strip out the [""]?
Cheers

returned_name and the other returned... variables is an array of strings caused by the map function. Printing an array using the description method displays "[item1, item2, item3]". To print each string separately you need a repeat loop.
let returnedArray = resString["message"].arrayValue // you might cast the object to `[[String:Any]]`
for item in returnedArray {
print(item["name"])
print(item["lockTime"])
print(item["type"])
}
To display the values in a table view you need a model, the best way is a custom struct
struct Item {
let name : String
let lockTime : String
let type : String
}
Then create a data source array
var items : [Item]()
And map your received JSON to the model
let returnedArray = resString["message"].arrayValue
for item in returnedArray {
let name = item["name"] ?? ""
let lockTime = item["lockTime"] ?? ""
let type = item["type"] ?? ""
items.append(Item(name:name, lockTime:lockTime, type:type))
}
Put the code to parse the JSON in viewDidLoad and call reloadData() at the end, in cellForRow get the item for the row from the array and display the values for example:
let item = items[indexPath.row]
self.updatename!.text = item.name
self.updateLock!.text = item.lockTime
self.updateText!.text = item.type
PS: since you are new to Swift don't use snake case variable names (returned_name), use camel case (returnedName).

Related

Multiple File Upload in Swift

I am trying to upload multiple file to the server by my iPhone. The problem is that when I try to upload 1.4 mb file it could not upload properly. I checked the uploaded file and the file size is 1 kb. So I made this code based on the postman. As you know the postman can create the code according to which language do you want. Thank you.
function uploadFile(){
var parameters: [[String: Any]] = []
var fileCount: Int = 0
let homeDirectory = ""
var soundPath = "\(homeDirectory)\(CommonUtil.PATH_SOUND)"
soundPath = soundPath.replacingOccurrences(of: "file:///", with: "")
//fiels[0] = ["fileName" : "2021_10_19_09_12_52.wav"]
//fiels[1] = ["fileName" : "2021_10_19_09_12_53.wav"]
//fiels[3] = ["fileName" : "2021_10_19_09_12_54.wav"]
for item in files{
var dict = item as! [String:String]
let strFilePath = "\(soundPath)\(dict["fileName"]!)"
if FileManager.default.fileExists(atPath: strFilePath){
var dict = [String:Any]()
dict.updateValue("strFileName[\(fileCount)]", forKey: "key")
dict.updateValue(strFilePath, forKey: "src")
dict.updateValue("file", forKey: "type")
parameters.append(dict)
fileCount++
}
print(dict["fileName"]!)
}
var dict = [String:Any]()
dict.updateValue("strApiName", forKey: "key")
dict.updateValue("soundFileUpload", forKey: "value")
dict.updateValue("text", forKey: "type")
parameters.append(dict)
uploadFiles(parameters: parameters)
}
func uploadFiles(parameters: [[String: Any]]){
var semaphore = DispatchSemaphore(value: 0)
let boundary = "Boundary - \(UUID().uuidString)"
var body = ""
var error: Error? = nil
for param in parameters {
if param["disabled"] == nil {
let paramName = param["key"]!
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(paramName)\""
if param["contentType"] != nil {
body += "\r\nContent-Type: \(param["contentType"] as! String)"
}
let paramType = param["type"] as! String
if paramType == "text" {
let paramValue = param["value"] as! String
body += "\r\n\r\n\(paramValue)\r\n"
} else {
let paramSrc = param["src"] as! String
//let fileData = URL(string: paramSrc)?.dataRepresentation ?? Data()
let fileData = try! NSData(contentsOfFile:paramSrc, options:[]) as Data
let fileContent = String(data: fileData, encoding: .utf8)
body += "; filename=\"\("file:///"+paramSrc)\"\r\n"
+ "Content-Type: \"content-type header\"\r\n\r\n\(fileContent)\r\n"
}
}
}
body += "--\(boundary)--\r\n";
let postData = body.data(using: .utf8)
var request = URLRequest(url: URL(string: "http://api.polytus.com/")!,timeoutInterval: Double.infinity)
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
DispatchQueue.main.async {
self.showLoading(show: false)
self.showBluetoothAlert(message: "failed")
}
print(String(describing: error))
semaphore.signal()
return
}
DispatchQueue.main.async {
self.showLoading(show: false)
self.showBluetoothAlert(message: "success")
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()
}
For what I've understand you want to know the file size in the server filesystem. If this is you want you have to use an API that give you back the data, after the file is uploaded and moved from temporary web server space to final location. In brief you need to do 2 request, one for upload and one for getting the file data (space or any other data needed), or develop an API that give you as answer of the correct upload the file size.

How to make a url post request which is returned by the function in Swift

Hi guys I am trying to contact my Rest API and get the data. I am successful in doing that but I want the function to return the string that it obtained.
This is why code so far:
private func getPost(one: String, two: String, link: String) {
let url = URL(string: link)!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
"parent" : one,
"original": two
]
request.httpBody = parameters.percentEncoded()
var responseString = ""
print("Sarcasm \(yourMessage) \(otherMessage) \(link)")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
error == nil else { // check for fundamental networking error
print("error", error ?? "Unknown error")
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
responseString = String(data: data, encoding: .utf8)!
print("responseString = \(responseString)")
// return responseString
}
task.resume()
}
Where :
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
All I want is this function (getPost) to return the response string that it obtains from the post request. However, I do not know what to do. I mean the application gets the response string from the post request but then I want to modify the function so that it returns it instead of printing it.

Method to return value retrieved from HTTP request [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I have a method which performs an HTTP request and retrieves data from a website, it's working as expected, I'm getting the data correctly. What I haven't been able to do is return the retrieved value when the method is called.
Here is the code...
func myFunction(zipCode: String)->String{
var myData:String = ""
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
return
}
myData = jsonArray[0]["MyPropertyName"]!
// Here, myData outputs, "Info for zip code 52484 from HTTP request"
}
task.resume()
return myData
}
When I call myFunction I get and empty string...
myFunction(zipCode: "52484")// Outputs an empty string
What I was expecting to see is a return value of "Info for zip code 52484 from HTTP request" since the myData variable was modified inside the let task = before the return call. I tried returning inside the let task = but this cannot be done in Swift.
How can I return the retrieved value when the myFunction is called?
You need a completion as request is asynchronous
func myFunction(zipCode: String,completion:#escaping(_ str:String?) -> () ) {
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
completion(nil)
return
}
guard let data = data else {
print("Data is empty")
completion(nil)
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
completion(nil)
return
}
let myData = jsonArray[0]["MyPropertyName"]!
completion(myData)
}
task.resume()
}
Call
myFunction(zipCode: "52484") { (str) in
if let st = str {
print(st)
}
}

How to assign elements of a dictionary to JSON object in Vapor 3?

In Vapor 1.5 I used to assign the elements of an existing dictionary to a JSON object as shown below. How can I do this Vapor 3?
func makeCustomJSON(jsonFromReading: JSON, clientData: DataFromClient) throws -> JSON{
var dictionaryOfStrings = [String:String]()
dictionaryOfStrings["ChangesMadeBy"] = "Cleaner"
dictionaryOfStrings["BookingNumber"] = clientData.bookingNumber
dictionaryOfStrings["Claimed"] = "false"
//some 50 properties more...
//object read from /Users
var finalJsonObj = jsonFromReading
//assign the values of dictionaryOfStrings to finalJsonObj
for i in dictionaryOfStrings {
let key = i.key
let value = i.value
finalJsonObj[key] = try JSON(node:value)
}
//make json from object under CancelledBy and assign it to arrayOfTimeStampObjs
var arrayOfTimeStampObjs = try jsonFromReading["CancelledBy"]?.makeJSON() ?? JSON(node:[String:Node]())
//assign dictionaryOfStrings to current time stamp when booking is claimed
arrayOfTimeStampObjs[clientData.timeStampBookingCancelledByCleaner] = try JSON(node:dictionaryOfStrings)
finalJsonObj["CancelledBy"] = arrayOfTimeStampObjs
return finalJsonObj
} //end of makeCustomJSON
This is basically, JSON serialization in swift. Decoding the JSON object to a Dictionary and then modifying the dictionary and creating a new JSON.
router.get("test") { req -> String in
let jsonDic = ["name":"Alan"]
let data = try JSONSerialization.data(withJSONObject: jsonDic, options: .prettyPrinted)
let jsonString = String(data: data, encoding: .utf8)
return jsonString ?? "FAILED"
}
router.get("test2") { req -> String in
do {
// Loading existing JSON
guard let url = URL(string: "http://localhost:8080/test") else {
return "Invalid URL"
}
let jsonData = try Data(contentsOf: url)
guard var jsonDic = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String:String] else {
return "JSONSerialization Failed"
}
// Apply Changes
jsonDic["name"] = "John"
// Creating new JSON object
let data = try JSONSerialization.data(withJSONObject: jsonDic, options: .prettyPrinted)
let jsonString = String(data: data, encoding: .utf8)
return jsonString ?? "FAILED"
}catch{
return "ERROR"
}
}
I strongly recommend to create struct or class for your data type. It would be much safer in casting using the codable protocol and much easier to convert between JSON and your objects type because of the content protocol in vapor version 3.

How to pass two parameter into webservice

I have web service
http://dev.nsol.sg/projects/sneakers/Api/get_single_news
Method: POST
Required Parameters:
userid
newsid
Response:
success = 0 (error), 1 (success)
message = Description of result if success = 0 then message will have the detail description
data: if success = 1 then we will have complete details of common requirements like single news
newsurl: News Image URL + Concatenate Value of img from DB
Here is my try to fetch data from url
func single_news(userid: Int) {
var request = URLRequest(url: URL(string: news_url)!)
request.httpMethod = "POST"
//Pass your parameter here
let postString = "userid=\(userid)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("error=(error)")
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
print("abcnews")
//here is your JSON
print(json)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
}
task.resume()
}
How to pass the second parameter newsid.You can download the project from this link https://drive.google.com/file/d/1mBlYcNaW8K3s6Y2V6BY6zdM0Znvyw3Si/view?usp=sharing
Rewrite your function
func single_news(userid: Int , newsid : Int) {}
I assume that newsid is also an Int value.
And write your postString like this...
let postString = "userid=\(userid)&newsid=\(newsid)"
I hope this will help you.
If I understood correctly, you just want to create and send a JSON object with two properties.
You can do it like this:
let body: [String: Any] = ["userid": userid,
"newsid": newsid]
request.httpBody = try! JSONSerialization.data(withJSONObject: body, options: [])