Hi i am new to IOS App developement.
My code is
func sendRequest<T: Decodable>(api: String, parameters: [String: String]? = nil, outputBlock: #escaping (T) -> () ) {
guard let url = URL(string: "http://xxyyzz.com/appRegister.php") else {return}
print("hitting : -", url.absoluteString)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let newparam = ["name": "rr", "pass": "123456", "email": "rr#rr.com", "passConfirm":"123456"]
let httpBody = try? JSONSerialization.data(withJSONObject: newparam)
request.httpBody = httpBody
if let data = request.httpBody, let str = String(data: data, encoding: String.Encoding.utf8) {
print(str)
}
URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
DispatchQueue.main.async {
Indicator.shared.hideProgressView()
if let err = error {
print(err.localizedDescription)
return
}
guard let data = data else {return}
do {
let obj = String(data: data, encoding: String.Encoding.utf8)
print(obj ?? "oberrrrr")
}
}
}.resume()
}
and console printed result as per code is below
hitting : - http://xxyyzz.com/appRegister.php
{"email":"rr#rr.com","passConfirm":"123456","name":"rr","pass":"123456"}
{"error":"Please enter all fields."}
url and parameters works well on postman that means their is something missing in my code.
just to answer the problem if anyone else faces this.
this code is fine but the problem was with php web-service as the backend developer was not accepting json values as parameter instead form data was need to send.
So, two types of fix can be made here
accept json at backend by adding :-
$postdata = file_get_contents("php://input");
$request = json_decode($postdata, true);
send form data instead json
func sendRequest<T: Decodable>(api: String, parameters: [String: Any]? = nil, outputBlock: #escaping (T) -> () ) {
guard let url = URL(string: api) else {return}
print("hitting : -", url.absoluteString)
var request = URLRequest(url: url)
if let parameters = parameters {
request.httpMethod = "POST"
var postArr = [String]()
for(key, value) in parameters
{
postArr.append(key + "=\(value)")
}
let postString = postArr.map { String($0) }.joined(separator: "&")
request.httpBody = postString.data(using: .utf8)
if let data = request.httpBody, let str = String(data: data, encoding: String.Encoding.utf8) {
print(str)
}
}
URLSession.shared.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
Indicator.shared.hideProgressView()
if let err = error {
print(err.localizedDescription)
return
}
guard let data = data else {return}
do {
let obj = try JSONDecoder().decode(T.self, from: data)
outputBlock(obj)
} catch let jsonErr {
print(jsonErr)
}
}
}.resume()
}
Related
I'm using a generic function for POST requests in my app. I have the following function:
func PostRequest<In:Codable>(object: In, endpoint: String){
do{
let url = URL(string: "http://localhost:8080/\(endpoint)/")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.httpBody = try JSONEncoder().encode(object)
URLSession.shared.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else{
print(error?.localizedDescription ?? "No Data")
return
}
let JSONResponse = try? JSONSerialization.jsonObject(with: data, options: [])
if let JSONResponse = JSONResponse as? [String: Any] {
print(JSONResponse)
}
}.resume()
}catch{
print(error)
}
}
And in this case, the object is the following struct:
struct MarkAsDelivered: Codable{
let whoCollected: String
let deliveryID: Int
}
When I print the result of JSONEncoder().encode(object) as a string, it returns the following, as would be expected:
{
"whoCollected":"TESTNAME",
"deliveryID":140
}
however, when i view this JSON object on my backend, it returns it as the following:
{
"{\"whoCollected\":\"TESTNAME\",\"deliveryID\":140}" : ""
}
From what I can tell, it is using the JSON object as a key.
Does anyone know what has caused this issue. Any assistance would be greatly appreciated.
as #burnsi mentioned in the comments to my question, I was indeed missing the content type. Specifying it fixed my issue:
func PostRequest<In:Codable>(object: In, endpoint: String){
do{
let url = URL(string: "http://localhost:8080/\(endpoint)/")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
//Added content type on line below:
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(object)
URLSession.shared.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else{
print(error?.localizedDescription ?? "No Data")
return
}
let JSONResponse = try? JSONSerialization.jsonObject(with: data, options: [])
if let JSONResponse = JSONResponse as? [String: Any] {
print(JSONResponse)
}
}.resume()
}catch{
print(error)
}
}
I'm new to Swift and I am trying to refactor my URL Post requests. I have multiple URL POST requests inside the same View Controller like this. Everything works fine but it seems to me that there is a lot of repetitive code that could be reused. Particularly, I don't know how to pass/handle different Data Models that should be used in parseRequest1 and parseRequest2. I also read that there should be only one session used for URL requests within the same project. Any help would be greatly appreciate it!
func request1() {
let parameters = [...//some parameters to send]
guard let url = URL(string: "https//www.....") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: [])
else {
print("Error")
return
}
request.httpBody = parametersToSend
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
self.parseRequest1(data: safeData)
}
}.resume()
}
func parseRequest1(data: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(DataModelForRequest1.self, from: data)
DispatchQueue.main.async {
self.performAction1(request1Result)
}
} catch {
print(error)
}
}
Then I have another URL request request2 which is almost identical except the parameters, and model to be used for decoding and action inside parseRequest2.
func request2() {
let parameters = [...//some parameters to send]
guard let url = URL(string: "https//www.....") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: [])
else {
print("Error")
return
}
request.httpBody = parametersToSend
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
self.parseRequest2(data: safeData)
}
}.resume()
}
func parseRequest2(data: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(DataModelForRequest2.self, from: data)
DispatchQueue.main.async {
self.performAction2(request2Result)
}
} catch {
print(error)
}
}
The only differences seem to be:
request parameters
type of model returned
the action you do after the response is received
This means that we can write this as one single method taking the above three values as parameters:
func request<T: Codable>(modelType: T.Type, parameters: [String: Any], completion: (T) -> Void) {
func parseResponse(data: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(T.self, from: data)
DispatchQueue.main.async {
completion(decodedData)
}
} catch {
print(error)
}
}
guard let url = URL(string: "https//www.....") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: [])
else {
print("Error")
return
}
request.httpBody = parametersToSend
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
parseResponse(data: safeData)
}
}.resume()
}
You can then call this method with the appropriate parameters as per your needs.
In GET request, I am trying to add header.
request.setValue(value: String?, forHTTPHeaderField: "SessionInfo")
But, SessionInfo value is in [String : Any]
Attempt 1:
func SO_Function() {
let service_url = WebService.sharedManager().serviceURL
let getMenuURL = service_url + "/MyPage/FileDownload.ashx"
var convertedString = ""
do {
let data1 = try JSONSerialization.data(withJSONObject: WebService.sharedManager().mobInfoDict, options: [])
convertedString = String(data: data1, encoding: .utf8)!
print("\n\nconvertedJSONtoData ", convertedString)
} catch {
print("\n\nCAtcLL___Err ",error.localizedDescription)
}
let url = NSURL(string: getMenuURL)
let request = NSMutableURLRequest(url: url! as URL)
//Below line, If I can able to add [String:Any] then I will get
proper Image as output.
request.setValue(convertedString, forHTTPHeaderField: "SessionInfo")
request.setValue("67a2a6fb1d13450a", forHTTPHeaderField: "Flowid")
request.setValue("d29566ac42de4e99", forHTTPHeaderField: "Fileid")
request.setValue("LMS_LEAVEREQUEST", forHTTPHeaderField: "Form")
request.httpMethod = "GET"
let session = URLSession.shared
let mData = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
if let res = response as? HTTPURLResponse {
DispatchQueue.main.async {
let img = UIImage(data: data!)
self.attachmentImgVw.image = img
}
}else{
print("\n\nError: \(String(describing: error))")
}
}
mData.resume()
}
Output
The data couldn’t be read because it isn’t in the correct format.
Error ScreenShot 1:
Postman Screenshot
In postman, I am giving SessionInfo value as in Dictionary format. Working fine.
How to solve this issue?
I'm trying to make an HTTP post request with params set in a dictionary here's my dict
let parameters = [
["name": "tag","value": "login"],
["name": "email","value": "s#s.com"],
["name": "password","value": "aaaa"]
]
but I don't to know how to access it in hers's my complete request function
func data_request(_ url:String)
{
let parameter = [
["name": "tag","value": "login"],
["name": "email","value": "s#s.com"],
["name": "password","value": "aaaa"]
]
let url:NSURL = NSURL(string: url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
let paramString = parameter?
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
guard let _:NSData = data as NSData?, let _:URLResponse = response, error == nil else {
print("error")
return
}
if let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
{
print(dataString)
}
}
task.resume()
}
Need to convert dictionary to json string like below:
let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
After that pass to the http.Body
// insert json data to the request
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.HTTPBody = jsonData
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
return task
} catch {
print(error)
}
You need to convert the Dictionary into Data and set it to httpBody
you could solve in this way
let paramData = try? JSONSerialization.data(withJSONObject: parameter, options: [])
request.httpBody = paramData
I am learning swift and I send a request to the server with the code below. It works for simple request and I get response from the server. My problem is I can not send a file to server.
code :
let parameters = parameter
let request = NSMutableURLRequest(URL: NSURL(string: requestUrl)!)
let boundaryConstant = "-----Boundary+\(arc4random())\(arc4random())"
let contentType = "multipart/form-data; boundary=" + boundaryConstant
let boundaryStart = "--\(boundaryConstant)\r\n"
let boundaryEnd = "--\(boundaryConstant)--\r\n"
let body:NSMutableString = NSMutableString();
for (key, value) in parameters {
body.appendFormat(boundaryStart)
body.appendFormat("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendFormat("\(value)\r\n")
}
body.appendFormat(boundaryEnd)
request.HTTPMethod = "POST"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
// check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
self.responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
print("MMMMMMMM \(self.responseString)")
self.result = self.responseString.dataUsingEncoding(NSUTF8StringEncoding)! as NSData
callback(self.responseString)
}
print("code start")
task.resume()
result :
i can post file to server by this code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let request = createRequest()
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
if error != nil {
// handle error here
print(error)
return
}
do {
if let responseDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("success == \(responseDictionary)")
}
} catch {
print(error)
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
}
task.resume()
}
func createRequest () -> NSURLRequest {
let param = []
let boundary = generateBoundaryString()
let url = NSURL(string: "URl")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.setValue("userValue", forHTTPHeaderField: "X-Client-user")
request.setValue("passValue", forHTTPHeaderField: "X-Access-pass")
//let path1 = NSBundle.mainBundle().pathForResource("voice", ofType: "png") as String!
request.HTTPBody = createBodyWithParameters(param, filePathKey: "voice", paths: ["pathURl"], boundary: boundary)
return request
}
func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, paths: [String]?, boundary: String) -> NSData {
let body = NSMutableData()
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
if paths != nil {
for path in paths! {
let url = NSURL(fileURLWithPath: path)
let filename = url.lastPathComponent
let data = NSData(contentsOfURL: url)!
let mimetype = mimeTypeForPath(path)
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename!)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.appendData(data)
body.appendString("\r\n")
}
}
body.appendString("--\(boundary)--\r\n")
return body
}
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().UUIDString)"
}
func mimeTypeForPath(path: String) -> String {
let url = NSURL(fileURLWithPath: path)
let pathExtension = url.pathExtension
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension! as NSString, nil)?.takeRetainedValue() {
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
return mimetype as String
}
}
return "application/octet-stream";
}
As you read here, you should use NSURLSession for HTTP work, it far more flexible and powerful; and I think is destined to replace NSURLconnection...
https://www.objc.io/issues/5-ios7/from-nsurlconnection-to-nsurlsession/
Here is a example for you...
func getMetaData(lePath:String, completion: (string: String?, error: ErrorType?) -> Void) {
// **** get_metadata ****
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.dropboxapi.com/2/files/get_metadata")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("Bearer ab-blah-blah", forHTTPHeaderField: "Authorization")
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
request.addValue("path", forHTTPHeaderField: lePath)
let cursor:NSDictionary? = ["path":lePath]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(cursor!, options: [])
request.HTTPBody = jsonData
print("json ",jsonData)
} catch {
print("snafoo alert")
}
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if let error = error {
completion(string: nil, error: error)
return
}
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Body: \(strData)\n\n")
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers);
self.jsonParser(jsonResult,field2file: "ignore")
for (key, value) in self.parsedJson {
print("key2 \(key) value2 \(value)")
}
completion(string: "", error: nil)
} catch {
completion(string: nil, error: error)
}
})
task.resume()
}
Great answer above.. Here it's updated for Swift3:
func getMetaData(lePath:String, completion: (string: String?, error: ErrorType?) -> Void) {
// **** get_metadata ****
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.dropboxapi.com/2/files/get_metadata")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("Bearer ab-blah-blah", forHTTPHeaderField: "Authorization")
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
request.addValue("path", forHTTPHeaderField: lePath)
let cursor:NSDictionary? = ["path":lePath]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(cursor!, options: [])
request.HTTPBody = jsonData
print("json ",jsonData)
} catch {
print("snafoo alert")
}
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if let error = error {
completion(string: nil, error: error)
return
}
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Body: \(strData)\n\n")
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers);
self.jsonParser(jsonResult,field2file: "ignore")
for (key, value) in self.parsedJson {
print("key2 \(key) value2 \(value)")
}
completion(string: "", error: nil)
} catch {
completion(string: nil, error: error)
}
})
task.resume()
}