swiftswift4get-requestnsurlcomponents

Swift GET request with url parameters


I am building a survey where when a user answers a question I append the answers to a url string with parameters and send a get request to my server. So for every answer there is a record made of the selected answer, timestamp and unique id of the survey.

I am not sure the best way to do this, but this is what I have so far.

I create the url and query items.

var urlComponents: URLComponents {

    let resultID = surveyQuestions.resultId
    print("\(String(describing: resultID))")

    let resultResponseID = surveyQuestions.questions[surveyResultResponseId]
    print("\(String(describing: resultResponseID))")

    let questionIndex = questionNumbers
    print("\(String(describing: questionIndex))")

    var urlComponents = URLComponents(string: "My String URL")

    urlComponents?.queryItems = [
        URLQueryItem(name: "surveyResultsId", value: "\(String(describing: resultID))"),
        URLQueryItem(name: "surveyResultsResponseId", value: "\(String(describing: resultResponseID))"),
        URLQueryItem(name: "questions", value: "\(questionIndex)"),
        URLQueryItem(name: "selectedAnswer", value: "\(storedAnswer)")

    ] 

    let url = urlComponents?.url

    print(url!.absoluteString as Any)

    return urlComponents!
}

Then I build the send request.

func sendRequest(_ url: String, parameters: [String: String], completion: @escaping ([String: Any]?, Error?) -> Void) {

    var components = URLComponents(string: url)!
    components.queryItems = parameters.map { (key, value) in
        URLQueryItem(name: key, value: value)
    }

    components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
    let request = URLRequest(url: components.url!)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, // is there data
            let response = response as? HTTPURLResponse, // is there HTTP response
            (200 ..< 300) ~= response.statusCode, // is statusCode 2XX
            error == nil else { // was there no error, otherwise ...
                completion(nil, error)
                return
        }

        let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
        completion(responseObject, nil)
        print("This is the \(responseObject!)")
    }
    task.resume()
}

And finally I call the send request when an answer is pressed.

@IBAction func answerPressed(_ sender: UIButton) {

    if sender.tag == selectedAnswer {
        questionNumbers += 1
    }
    storedAnswer = [sender.tag]
//        storedAnswer.append(sender.tag)
    print(storedAnswer)

    sendRequest("\(urlComponents)", parameters: ["": ""]) { responseObject, error in
        guard let responseObject = responseObject, error == nil else {
            print(error ?? "Unknown error")
            return
        }


        // use `responseObject` here
    }

    questionNumbers += 1
    updateQuestion()
}

Now when I run this I get back the string with the query items, but when I run the send request I get unknown error. I feel as if I am doing something wrong. For the area "use responseObject here" what do I put in there. Im a little confused. Also when I call the send request what should I put in the parameter values. Right now they are just parameters: ["": ""]. I feel as if I am close. Any help is much appreciated.


Solution

  • First off, you're doing a lot more work than is probably necessary. You're encoding your query string parameters into URLComponents which is correct. Then, in your send you are decomposing your URL and parsing out the components then re-encoding them. You're also doing a lot of force-unwrapping, which is fragile and hides problems.

    Here's your code simplified in a playground that works for me:

    import UIKit
    import PlaygroundSupport
    
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    let sampleURL = "https://someserver.com/somepath"
    
    func sendRequest(_ url: URL, completion: @escaping ([String: Any]?, Error?) -> Void) {
    
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, // is there data
                let response = response as? HTTPURLResponse, // is there HTTP response
                (200 ..< 300) ~= response.statusCode, // is statusCode 2XX
                error == nil else { // was there no error, otherwise ...
                    completion(nil, error)
                    return
            }
            let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
            completion(responseObject, nil)
        }
        task.resume()
    }
    
    var urlComponents: URLComponents? {
    
        let resultID = "resultID123"
        let resultResponseID = "responseID456"
        let questionIndex = "questionNumbers1"
    
        var urlComponents = URLComponents(string: sampleURL)
        urlComponents?.queryItems = [
            URLQueryItem(name: "surveyResultsId", value: "\(String(describing: resultID))"),
            URLQueryItem(name: "surveyResultsResponseId", value: "\(String(describing: resultResponseID))"),
            URLQueryItem(name: "questions", value: "\(questionIndex)"),
            URLQueryItem(name: "selectedAnswer", value: "\("storedAnswer1")")
    
        ]
        return urlComponents
    }
    
    if let urlComponents = urlComponents, let url = urlComponents.url?.absoluteURL {
        sendRequest(url) { (result, error) in
            print("Got an answer: \(String(describing: result))")
        }
    }
    

    When I run this against a server URL that returns valid JSON, I get:

    Got an answer: Optional(["image": {
        href = "https://example.com";
    }, "object_types": {
        card =     {
            fields =         {
            };
            pollable = 1;
        };
    }])