Home > OS >  How can I make a POST Request in Swift with parameters using URLSession
How can I make a POST Request in Swift with parameters using URLSession

Time:12-24

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?

CodePudding user response:

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

CodePudding user response:

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