How can I make a POST Request in Swift with parameters using URLSession - swift

I have a post request that I want to make using URLSession.
The post request looks like this:
curl -X POST 'https://smartdevicemanagement.googleapis.com/v1/enterprises/privatekey/devices/devicekey:executeCommand' -H 'Content-Type: application/json' -H 'Authorization: authtoken' --data-raw '{
"command" : "sdm.devices.commands",
"params" : {
"commandName" : "cmdValue"
}
}'
As this is a POST request, I want to only decode if the response is an error message.
Here is the code I currently have:
guard let url = URL(string: "https://smartdevicemanagement.googleapis.com/v1/enterprises/\(project_id)/devices") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("token", forHTTPHeaderField: "Authorization")
let cmdParams: [String: String] = ["command":"sdm.devices.commands", "params" : ["commandName": "cmdValue"]]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: cmdParams)
} catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else {print(error!.localizedDescription); return }
guard let data = data else {print("empty data"); return }
The cmdParams are throwing an error, so I'm not sure how to structure the params request properly, a successful POST will result in the API returning {} an unsuccessful request will return some error.
How can I adjust my code to get this working?

You need to encode the JSON string as data. Then you can add it as the httpBody. Don't forget to add the token to the request.
// Encode your JSON data
let jsonString = "{ \"command\" : \"sdm.devices.commands\", \"params\" : { \"commandName\" : \"cmdValue\" } }"
guard let jsonData = jsonString.data(using: .utf8) else { return }
// Send request
guard let url = URL(string: "https://smartdevicemanagement.googleapis.com/v1/enterprises/\(project_id)/devices") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("token", forHTTPHeaderField: "Authorization") // Most likely you want to add some token here
// request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
// Handle HTTP request error
} else if let data = data {
// Handle HTTP request response
} else {
// Handle unexpected error
}
}
task.resume()

You could try using "urlencoded" to encode your request body. Here is my test code:
(note, since I do not have a paid subscription to this service I cannot fully test my code)
struct ContentView: View {
let project_id = 123 // <-- adjust to your needs
var body: some View {
Text("testing")
.onAppear {
if let url = URL(string: "https://smartdevicemanagement.googleapis.com/v1/enterprises/\(project_id)/devices") {
doPOST(url: url)
}
}
}
func doPOST(url: URL) {
var request = URLRequest(url: url)
request.httpMethod = "POST"
// try urlencoding
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("token", forHTTPHeaderField: "Authorization") // <-- your api "token" here
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
components.queryItems = [
URLQueryItem(name: "command", value: "sdm.devices.commands"),
URLQueryItem(name: "params", value: "{ \"commandName\" : \"cmdValue\" }")
]
if let query = components.url!.query {
print("--> query: \(query)")
request.httpBody = Data(query.utf8)
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
showResponse(data) // <-- for debuging
guard error == nil else { print("--> error: \(error)"); return }
guard let data = data else { print("empty data"); return }
}
task.resume()
}
func showResponse(_ data: Data?) {
if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers), let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) {
print("\n---> response: " + String(decoding: jsonData, as: UTF8.self))
} else {
print("=========> error")
}
}
}
If this does not work, have a look at this doc:
https://developers.google.com/nest/device-access/reference/rest/v1/enterprises.devices/executeCommand
In particular: The URL uses gRPC Transcoding syntax. It may be relevant.

Related

How to read stream using Openai completion API in Swift - iOS?

I am trying to read the output in stream (as it comes) rather than waiting up for the whole response.
I have used the following code.
func caller() {
let completionAPIURL = URL(string: "https://api.openai.com/v1/completions")!
var request = URLRequest(url: completionAPIURL)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("<Token>", forHTTPHeaderField: "Authorization")
let requestBody = """
{
"model": "<model>",
"prompt": "<prompt>",
"max_tokens": 200,
"stream": true
}
"""
request.httpBody = requestBody.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Error: \(error)")
return
}
print((response as? HTTPURLResponse)?.statusCode)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Error: invalid HTTP response code")
return
}
guard let data = data else {
print("Error: missing response data")
return
}
let eventStreamString = String(data: data, encoding: .utf8)!
let eventStreamLines = eventStreamString.components(separatedBy: "\n")
for event in eventStreamLines {
let eventFields = event.components(separatedBy: ":")
if eventFields.count < 2 {
continue
}
if fieldName == "data" {
if fieldValue == "[DONE]" {
// Stream terminated
break
} else {
// Process event data
print(event)
}
}
}
}
task.resume()
}
The problem is, it still waits till whole response is in place and then prints the data. What I am trying to achieve is to get the data as and how stream is generated, and not after the whole response is in place.
I am comparatively newbie and would expect a detailed help.

how to make get request with Authorization api key value in header Swift

below is my get request code. but getting 403 error ...please help!
func getUser(completion: #escaping(Result<User?, AuthenticationError>)-> Void) {
let loginVM = LoginViewModel()
let token = loginVM.accessToken
guard let url = URL(string: myURL) else {
completion(.failure(.custom(errorMessage: "URL is not correct")))
return
}
var request = URLRequest(url:url)
request.httpMethod = "GET"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
completion(.failure(.custom(errorMessage: "No data")))
return
}
guard let registerResponse = try? JSONDecoder().decode(User.self, from: data) else{
completion(.failure(.custom(errorMessage: "no response")))
return
}
completion(.success(User))
}.resume()
}
and instruction from my backend guy for this part is this :
I tested on Postman It work as like charm. It must be happen in my request code with Authorization....

what is unsupported grant type error in swift?

I'm trying to make a simple login post request with URLSession with my userDetails object as input and request is of content type "application/x-www-form-urlencoded", in response i am supposed to get an object of "access token", "refresh token", "userdetails".
but i keep getting:
error = "unsupported_grant_type"
request on postman works but something is not right when I make a request in my project. what am i doing wrong here?? API team says the input object has to be correct which is exactly from the postman.
func loginUser() {
/// login request here
let url = URL(string: EndPoint.loginUser)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let userData: [String: Any] = [
"UserName": "myUserEmail",
"Password": "myPassword",
"grant_type": "password"
]
guard let httpBody = try? JSONSerialization.data(withJSONObject: userData, options: .prettyPrinted) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, urlResponse, error) in
if let data = data {
do {
let jsonData = try JSONSerialization.jsonObject(with: data, options: [])
print(jsonData)
} catch let error {
debugPrint(error.localizedDescription)
}
}
}.resume()
}

urlrequest not sending post request

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

Swift parsing a http request returns domain error

okay, so Im trying to make a post request but when I try to parse the data, I always get a error domain code= 3840.
https://www.codepunker.com/tools/http-requests/64243-brrjq9t
In this example it shows what the request response looks like.
and this is my code:
let parameters = ["txt": "כאב גרון","usr": "","pass": "" ,"ktivmale": false] as [String : Any]
guard let url = URL(string: "http://www.nakdan.com/GetResult.aspx") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody
let task = URLSession.shared.dataTask(with: request as URLRequest){
data,response, error in
print(response!)
if error != nil{
print("error")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
DispatchQueue.main.async(execute: { () -> Void in
print(json)
SVProgressHUD.dismiss()
})
} catch {
print(error)
SVProgressHUD.dismiss()
SVProgressHUD.showError(withStatus: "Connection Failed.")
}
}
task.resume()